Skip to content
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
99 changes: 99 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Deploy Frontend & Backend

on:
push:
branches: [deploy]

jobs:
frontend:
name: Deploy Frontend to S3
runs-on: ubuntu-22.04

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '22'

- name: Install dependencies
run: |
cd frontend
npm ci

- name: Build frontend
run: |
cd frontend
npm run build

- name: Deploy to S3
uses: jakejarvis/s3-sync-action@master
with:
args: --delete
env:
AWS_S3_BUCKET: www.pirocheck.org
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
SOURCE_DIR: frontend/dist

backend:
name: Deploy Backend to EC2
needs: frontend
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Set up Java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'

- name: Build backend
run: |
cd backend
./gradlew build --no-daemon

- name: Restore PEM file
run: |
echo "${{ secrets.EC2_SSH_KEY }}" | base64 -d > pirocheck.pem
chmod 400 pirocheck.pem

- name: Copy JAR to EC2
run: |
scp -o StrictHostKeyChecking=no -i pirocheck.pem backend/build/libs/*.jar ubuntu@${{ secrets.EC2_HOST }}:/home/ubuntu/app.jar

- name: Restart Spring Boot on EC2
run: |
ssh -o StrictHostKeyChecking=no -i pirocheck.pem ubuntu@${{ secrets.EC2_HOST }} 'bash ~/restart.sh'


- name: Send Discord notification (Success)
if: success()
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d '{
"embeds": [{
"title": "🚀 Deploy 성공!",
"description": "**Branch**: `${{ github.ref }}`\n**Commit**: `${{ github.sha }}`\n🟢 서비스가 정상적으로 배포되었습니다!",
"color": 65353
}]
}' ${{ secrets.DISCORD_WEBHOOK }}

- name: Send Discord notification (Failure)
if: failure()
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d '{
"embeds": [{
"title": "❌ Deploy 실패!",
"description": "**Branch**: `${{ github.ref }}`\n**Commit**: `${{ github.sha }}`\n🔴 배포 중 오류가 발생했습니다. 로그를 확인하세요.",
"color": 16711680
}]
}' ${{ secrets.DISCORD_WEBHOOK }}
31 changes: 31 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Notify Discord on PR events

on:
pull_request:
types: [opened, closed, reopened]

jobs:
notify-discord:
runs-on: ubuntu-latest
steps:
- name: Determine PR event
id: msg
run: |
if [[ "${{ github.event.action }}" == "opened" ]]; then
echo "message= PR Opened: **${{ github.event.pull_request.title }}**" >> $GITHUB_OUTPUT
elif [[ "${{ github.event.action }}" == "reopened" ]]; then
echo "message= PR Reopened: **${{ github.event.pull_request.title }}**" >> $GITHUB_OUTPUT
elif [[ "${{ github.event.action }}" == "closed" && "${{ github.event.pull_request.merged }}" == "true" ]]; then
echo "message= PR Merged: **${{ github.event.pull_request.title }}**" >> $GITHUB_OUTPUT
else
echo "message= PR Closed without merge: **${{ github.event.pull_request.title }}**" >> $GITHUB_OUTPUT
fi

- name: Send Discord notification
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d "{\"content\": \"${{ steps.msg.outputs.message }}\\n[PR #${{ github.event.pull_request.number }}](${{ github.event.pull_request.html_url }}) by @${{ github.actor }}\"}" \
"$DISCORD_WEBHOOK_URL"
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@
# Common
.DS_Store
*.log
.env
.env

.idea/
4 changes: 3 additions & 1 deletion backend/pirocheck/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ build/
!**/src/main/**/build/
!**/src/test/**/build/

.idea
.idea/

### STS ###
.apt_generated
.classpath
Expand All @@ -18,7 +21,6 @@ bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package backend.pirocheck.User.repository;

import backend.pirocheck.User.entity.Role;
import backend.pirocheck.User.entity.User;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByName(String name);

List<User> findByRole(Role role);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package backend.pirocheck.attendence.controller;

import backend.pirocheck.attendence.dto.request.GetAttendanceByDateReq;
import backend.pirocheck.attendence.dto.request.MarkAttendanceReq;
import backend.pirocheck.attendence.dto.response.AttendanceSlotRes;
import backend.pirocheck.attendence.dto.response.AttendanceStatusRes;
import backend.pirocheck.attendence.entity.AttendanceCode;
import backend.pirocheck.attendence.service.AttendanceService;
import lombok.RequiredArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/attendance")
public class AttendanceController {

private final AttendanceService attendanceService;

// 특정 유저의 출석 정보
@GetMapping("/user")
public List<AttendanceStatusRes> getAttendanceByUserId(@RequestParam Long userId) {
return attendanceService.findByUserId(userId);
}

// 특정 유저의 특정 일자 출석 정보
@GetMapping("/user/date")
public List<AttendanceSlotRes> getAttendanceByUserIdAndDate(
@RequestParam Long userId,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date
) {
return attendanceService.findByUserIdAndDate(userId, LocalDate.now());
}

// 출석체크 시작
@PostMapping("/start")
public AttendanceCode postAttendance() {
return attendanceService.generateCodeAndCreateAttendances();
}

// 출석코드 비교
@PostMapping("/mark")
public boolean markAttendance(@RequestBody MarkAttendanceReq req) {
return attendanceService.markAttendance(req.getUserId(), req.getCode());
}

// 출석체크 종료
@PutMapping("/expire")
public boolean expireAttendance(@RequestParam String code) {
return attendanceService.exprireAttendanceCode(code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package backend.pirocheck.attendence.dto.request;

import lombok.Getter;

import java.time.LocalDate;

@Getter
public class GetAttendanceByDateReq {
private Long userId;
private LocalDate date;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package backend.pirocheck.attendence.dto.request;

import lombok.Getter;

@Getter
public class MarkAttendanceReq {
private Long userId;
private String code;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package backend.pirocheck.attendence.dto.response;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class AttendanceSlotRes {
private int order;
private boolean status;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package backend.pirocheck.attendence.dto.response;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDate;
import java.util.List;

@Getter
@Setter
public class AttendanceStatusRes {
private LocalDate date;
private List<AttendanceSlotRes> slots;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package backend.pirocheck.attendence.dto.response;

import lombok.Getter;

import java.time.LocalDate;

@Getter
public class GetAttendanceByUserIdRes {
private Long userId;
private LocalDate date;
private int order;
private boolean status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package backend.pirocheck.attendence.entity;

import backend.pirocheck.User.entity.User;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDate;

@Entity
@Table(
name = "attendance",
uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "date", "order_number"})
)
@Getter @Setter
public class Attendance {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;

private LocalDate date;

@Column(name = "order_number")
private int order;

private boolean status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package backend.pirocheck.attendence.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDate;

@Entity
@Table(name = "attendance_code")
@Getter @Setter
public class AttendanceCode {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String code;

private LocalDate date;

@Column(name = "order_number")
private int order;

private boolean isExpired = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package backend.pirocheck.attendence.repository;

import backend.pirocheck.attendence.entity.AttendanceCode;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.time.LocalDate;
import java.util.Optional;

@Repository
public interface AttendanceCodeRepository extends JpaRepository<AttendanceCode, Long> {
int countByDate(LocalDate date);
Optional<AttendanceCode> findByCodeAndDate(String code, LocalDate date);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package backend.pirocheck.attendence.repository;

import backend.pirocheck.attendence.entity.Attendance;
import backend.pirocheck.attendence.entity.AttendanceCode;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

@Repository
public interface AttendanceRepository extends JpaRepository<Attendance, Long> {
List<Attendance> findByUserId(Long userId);
List<Attendance> findByUserIdAndDate(Long userId, LocalDate date);
Optional<Attendance> findByUserIdAndDateAndOrder(Long userId, LocalDate date, int order);
}
Loading
Loading