Skip to content

Commit

Permalink
add: それなりに動くとこまで
Browse files Browse the repository at this point in the history
  • Loading branch information
diggymo committed Feb 3, 2024
1 parent e3ee94e commit 63f373f
Show file tree
Hide file tree
Showing 25 changed files with 326 additions and 62 deletions.
3 changes: 3 additions & 0 deletions backend/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ JWT_SECRET="jwt_secret"
TZ=Asia/Tokyo

LINE_CHANNEL_SECRET=""
LINE_CHANNEL_SECRET_FOR_MESSAGING=""

API_KEY=abcde
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"bcrypt": "^5.1.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"date-fns": "^3.3.1",
"helmet": "^6.0.1",
"jsonwebtoken": "^9.0.2",
"passport": "^0.6.0",
Expand Down
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { DynamodbModule } from './dynamodb/dynamodb.module';
import { FtftModule } from './ftft/ftft.module';
import { StorageModule } from './storage/storage.module';
import { SocialLoginModule } from './sociallogin/sociallogin.module';
import { ReminderModule } from './ftft/reminder/reminder.module';

@Module({
imports: [
Expand All @@ -20,6 +21,7 @@ import { SocialLoginModule } from './sociallogin/sociallogin.module';
StorageModule,
SocialLoginModule,
DynamodbModule,
ReminderModule,
FtftModule,
],
controllers: [AppController],
Expand Down
13 changes: 13 additions & 0 deletions backend/src/auth/guards/apikey.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class ApiKeyGuard implements CanActivate {
constructor(private configService: ConfigService) {}

canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const apiKey = request.headers['API-KEY'] || request.headers['api-key'];
return apiKey === this.configService.getOrThrow('API_KEY');
}
}
7 changes: 6 additions & 1 deletion backend/src/dynamodb/dynamodb.servuce.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { GetCommand, QueryCommand, PutCommand } from '@aws-sdk/lib-dynamodb';
import { GetCommand, QueryCommand, ScanCommand, PutCommand } from '@aws-sdk/lib-dynamodb';
import { Injectable } from '@nestjs/common';

@Injectable()
Expand Down Expand Up @@ -27,6 +27,11 @@ export class DynamodbService {
return result.Items ?? [];
}

public async scan(props: ConstructorParameters<typeof ScanCommand>[0]) {
const result = await this.client.send(new ScanCommand(props));
return result.Items ?? [];
}

public async createItem(props: ConstructorParameters<typeof PutCommand>[0]) {
const result = await this.client.send(new PutCommand(props));

Expand Down
16 changes: 8 additions & 8 deletions backend/src/entrypoint/_shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export const setAppConfig = async () => {
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
app.enableCors();

const config = new DocumentBuilder()
.setTitle('FTFT')
.setDescription('FTFT')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
// const config = new DocumentBuilder()
// .setTitle('FTFT')
// .setDescription('FTFT')
// .setVersion('1.0')
// .addBearerAuth()
// .build();
// const document = SwaggerModule.createDocument(app, config);
// SwaggerModule.setup('api', app, document);
return app;
};
1 change: 1 addition & 0 deletions backend/src/ftft/ftft.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ import { StorageModule } from 'src/storage/storage.module';
imports: [DynamodbModule, StorageModule],
controllers: [FtftController],
providers: [FtftService],
exports: [FtftService],
})
export class FtftModule {}
3 changes: 2 additions & 1 deletion backend/src/ftft/ftft.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,15 @@ export class FtftService {
});
}

async searchFtft(userId: string, offsetId?: string) {
async searchFtft(userId: string, offsetId?: string, limitSize?: number) {
const ftfts = await this.dynamoDb.query({
TableName: 'ftft',
IndexName: 'latest',
KeyConditionExpression: 'userId = :userId',
ExpressionAttributeValues: {
':userId': userId,
},
Limit: limitSize,
ExclusiveStartKey: offsetId !== undefined ? { id: offsetId } : undefined,
ScanIndexForward: false,
});
Expand Down
25 changes: 25 additions & 0 deletions backend/src/ftft/reminder/reminder.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Body, Post, Query } from '@nestjs/common';
import { Controller, Get, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import z from 'zod';
import { JwtAuthGuard } from 'src/auth/guards/jwt.guard';
import { AuthorizedUser, UserFragment } from 'src/user/user.decorator';
import { FtftService } from '../ftft.service';
import { ApiKeyGuard } from 'src/auth/guards/apikey.guard';
import { ReminderService } from './reminder.service';

@ApiTags('ftft/reminder')
@Controller('ftft/reminder')
export class ReminderController {
constructor(private readonly reminderService: ReminderService) {}

@Get('')
@ApiBearerAuth()
@UseGuards(ApiKeyGuard)
async searchFtfts(@Query() _query: any) {
console.log(_query);

await this.reminderService.remindToAllLineUsers();
return { '###': true };
}
}
12 changes: 12 additions & 0 deletions backend/src/ftft/reminder/reminder.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { FtftModule } from '../ftft.module';
import { ReminderController } from './reminder.controller';
import { ReminderService } from './reminder.service';
import { UserModule } from 'src/user/user.module';

@Module({
imports: [FtftModule, UserModule],
controllers: [ReminderController],
providers: [ReminderService],
})
export class ReminderModule {}
57 changes: 57 additions & 0 deletions backend/src/ftft/reminder/reminder.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Module } from '@nestjs/common';
import { FtftService } from '../ftft.service';
import { UserService } from 'src/user/user.service';
import { isPast, addDays, isFuture } from 'date-fns';
import { ConfigService } from '@nestjs/config';
import axios from 'axios';

@Module({})
export class ReminderService {
constructor(
private readonly userService: UserService,
private readonly ftftService: FtftService,
private readonly configService: ConfigService,
) {}

async remindToAllLineUsers() {
const lineUsers = await this.userService.findAllLineUsers();

console.log(lineUsers.length);
for (const lineUser of lineUsers) {
const ftftList = await this.ftftService.searchFtft(lineUser.id, undefined, 1);
console.log(ftftList);
if (ftftList.length === 0) {
continue;
}

// 3日以上前~4日以内か
const isTarget = isPast(addDays(ftftList[0].createdAt, 3)) && isFuture(addDays(ftftList[0].createdAt, 4));
if (!isTarget) {
continue;
}
await this.sendRemind(lineUser.lineUserId).catch((error) => {
console.error({ message: 'メッセージが送信できませんでした', error });
});
}
}

async sendRemind(lineUserId: string) {
return axios.post(
'https://api.line.me/v2/bot/message/push',
{
to: lineUserId,
messages: [
{
type: 'text',
text: '最近、何か新しいこと始めた?👀\nもしあればFTFTにメモってみよう🚀\nhttps://ftft.morifuji-is.ninja/',
},
],
},
{
headers: {
Authorization: 'Bearer ' + this.configService.getOrThrow('LINE_CHANNEL_SECRET_FOR_MESSAGING'),
},
},
);
}
}
9 changes: 9 additions & 0 deletions backend/src/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,13 @@ export class UserService {

return LineUserSchema.parse(lineUser[0]);
}

async findAllLineUsers() {
const lineUsers = await this.dynamoDb.scan({
TableName: TABLE_NAME,
IndexName: 'lineUserId',
});

return lineUsers.map((lineUser) => LineUserSchema.parse(lineUser));
}
}
5 changes: 5 additions & 0 deletions backend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3970,6 +3970,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"

date-fns@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.3.1.tgz#7581daca0892d139736697717a168afbb908cfed"
integrity sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==

debug@2.6.9:
version "2.6.9"
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
Expand Down
1 change: 1 addition & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FTFT</title>
<meta name="description" content="FTFTであなたの&quot;はじめて&quot;を記録しよう" />

<!-- favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@types/react-dom": "^18.2.17",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"@vitejs/plugin-basic-ssl": "^1.1.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"eslint": "^8.55.0",
"eslint-plugin-react-hooks": "^4.6.0",
Expand Down
12 changes: 12 additions & 0 deletions frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ErrorBoundary } from "react-error-boundary";
import { Login } from './Login'
import { LineLoginCallback } from './LineLoginCallback'
import { Setting } from './Setting'
import { LineLogin } from './LineLogin'

const queryClient = new QueryClient()

Expand All @@ -28,6 +29,10 @@ function App() {
path: "/login",
element: <Login />,
},
{
path: "/line-login",
element: <LineLogin/>,
},
{
path: "/line-login-callback",
element: <LineLoginCallback />,
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ export const Home = () => {
console.log(res.status)
if (res.status !== 201) throw new Error("送信できませんでした")

const message = content + "\n\n-------------\n\nFTFTであなたの\"はじめて\"を記録しよう✍️\n"
const message = `${content}${emoji ? `\n${emoji}` : ''}\n\n-------------\n\nFTFTであなたの"はじめて"を記録しよう✍️\n`
toast({
position: 'bottom',
title: "記録しました✍️",
description: <p>
友達に
<Link href={`https://line.me/R/share?text=${encodeURI(message + "https://ftft.morifuji-is.ninja/")}`}><u>LINE</u></Link>
<Link href={`https://line.me/R/share?text=${encodeURI(message + "https://liff.line.me/2003019183-014OmGVB")}`}><u>LINE VOOM</u></Link>
<Link href={`https://twitter.com/intent/tweet?text=${encodeURI(message)}&url=https://ftft.morifuji-is.ninja/`}><u>X</u></Link>
<Link href={`https://twitter.com/intent/tweet?text=${encodeURI(message)}&url=https://liff.line.me/2003019183-014OmGVB`}><u>X</u></Link>
で共有しよう🥰
</p>
})
Expand Down
93 changes: 93 additions & 0 deletions frontend/src/LineLogin.tsx

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion frontend/src/LineLoginCallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const LineLoginCallback = () => {
position: 'bottom',
status: "error",
title: "エラーが発生しました",
description: "時間をあけて再度お試しください"
description: "iPhoneではSafari、AndroidではChromeで開いて再度お試しください"
})
navigate({
pathname: '/login',
Expand Down
Loading

0 comments on commit 63f373f

Please sign in to comment.