├── iOS ├── FlipMate │ ├── Feature │ │ ├── .gitkeep │ │ ├── Chart │ │ │ ├── .gitignore │ │ │ ├── Tests │ │ │ │ └── ChartTests │ │ │ │ │ └── ChartTests.swift │ │ │ ├── Sources │ │ │ │ └── Chart │ │ │ │ │ └── Model │ │ │ │ │ └── WeeklySection.swift │ │ │ └── Package.swift │ │ ├── Category │ │ │ ├── .gitignore │ │ │ ├── Tests │ │ │ │ └── CategoryTests │ │ │ │ │ └── CategoryTests.swift │ │ │ └── Package.swift │ │ ├── MyPage │ │ │ ├── .gitignore │ │ │ ├── Tests │ │ │ │ └── MyPageTests │ │ │ │ │ └── MyPageTests.swift │ │ │ └── Package.swift │ │ └── Service │ │ │ ├── .gitignore │ │ │ ├── Tests │ │ │ └── ServiceTests │ │ │ │ └── ServiceTests.swift │ │ │ ├── Sources │ │ │ └── CategoryService │ │ │ │ └── CategoryServiceInterface │ │ │ │ └── CategoryManageable.swift │ │ │ └── Package.swift │ ├── FlipMate │ │ ├── Application │ │ │ ├── DIContainer │ │ │ │ ├── .gitkeep │ │ │ │ ├── AppDIContainer.swift │ │ │ │ └── ChartDIContainer.swift │ │ │ ├── AppDelegate.swift │ │ │ └── Coordinator │ │ │ │ └── ChartFlow │ │ │ │ └── ChartFlowCoordinator.swift │ │ ├── Presentation │ │ │ └── ChartScene │ │ │ │ └── .gitkeep │ │ ├── Data │ │ │ ├── Sources │ │ │ │ └── Data │ │ │ │ │ ├── Network │ │ │ │ │ ├── DataMapping │ │ │ │ │ │ ├── .gitkeep │ │ │ │ │ │ ├── TimerScene │ │ │ │ │ │ │ ├── TimeZoneRequestDTO.swift │ │ │ │ │ │ │ ├── TimerStartRequestDTO.swift │ │ │ │ │ │ │ ├── TimerFinishRequestDTO.swift │ │ │ │ │ │ │ ├── TimerStartResponseDTO.swift │ │ │ │ │ │ │ ├── TimerFinishResponseDTO.swift │ │ │ │ │ │ │ └── StudyLogResponseDTO.swift │ │ │ │ │ │ ├── StatusResponseDTO.swift │ │ │ │ │ │ ├── SocialScene │ │ │ │ │ │ │ ├── FriendUnfollowRequestDTO.swift │ │ │ │ │ │ │ ├── FriendFollowReqeustDTO.swift │ │ │ │ │ │ │ ├── SocialDetailRequestDTO.swift │ │ │ │ │ │ │ ├── FreindStatusResponseDTO.swift │ │ │ │ │ │ │ ├── UserProfileResposeDTO.swift │ │ │ │ │ │ │ ├── SocialDetailResponseDTO.swift │ │ │ │ │ │ │ └── FriendsResponseDTO.swift │ │ │ │ │ │ ├── StatusResponseWithErrorDTO.swift │ │ │ │ │ │ ├── LoginScene │ │ │ │ │ │ │ ├── GoogleAuthRequestDTO.swift │ │ │ │ │ │ │ ├── AppleAuthRequestDTO.swift │ │ │ │ │ │ │ ├── NickNameValidationResponseDTO.swift │ │ │ │ │ │ │ ├── AuthResponseDTO.swift │ │ │ │ │ │ │ ├── UserInfoResponseDTO.swift │ │ │ │ │ │ │ └── SignUpResponseDTO.swift │ │ │ │ │ │ ├── Category │ │ │ │ │ │ │ ├── CategoryRequestDTO.swift │ │ │ │ │ │ │ └── CategoryResponseDTO.swift │ │ │ │ │ │ └── ChartScene │ │ │ │ │ │ │ ├── WeeklyChartLogResponseDTO.swift │ │ │ │ │ │ │ └── DailyChartLogResponseDTO.swift │ │ │ │ │ ├── StudyLogEndpoints.swift │ │ │ │ │ ├── BaseComponents.swift │ │ │ │ │ ├── ChartEndpoints.swift │ │ │ │ │ ├── UserInfoEndpoints.swift │ │ │ │ │ ├── SocialEndpoints.swift │ │ │ │ │ ├── AuthenticationEndpoints.swift │ │ │ │ │ ├── TimerEndpoints.swift │ │ │ │ │ └── SignUpEndpoints.swift │ │ │ │ │ ├── Repositories │ │ │ │ │ ├── DefaultStudyLogRepository.swift │ │ │ │ │ ├── DefaultProfileSettingsRepository.swift │ │ │ │ │ └── DefaultUserInfoRepository.swift │ │ │ │ │ └── Extensions │ │ │ │ │ └── Data++Extension.swift │ │ │ ├── .gitignore │ │ │ ├── Tests │ │ │ │ └── DataTests │ │ │ │ │ └── DataTests.swift │ │ │ └── Package.swift │ │ ├── DesignSystem │ │ │ ├── Sources │ │ │ │ └── DesignSystem │ │ │ │ │ ├── Resources │ │ │ │ │ └── Assets.xcassets │ │ │ │ │ │ ├── Contents.json │ │ │ │ │ │ ├── FilpMateColor │ │ │ │ │ │ ├── Contents.json │ │ │ │ │ │ ├── DarkBlue.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── Gray2.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── Gray4.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── Gray5.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── TabBarIconUnSelected.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── Gray1.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── Gray3.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── TabBarColor.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── ApproveGreen.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── TabBarLayerColor.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── WarningRed.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ └── TabBarIconSelected.colorset │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── AppIcon.appiconset │ │ │ │ │ │ ├── AppIcon.png │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── AccentColor.colorset │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── FlipMate_icon.imageset │ │ │ │ │ │ ├── FlipMate_icon 1.png │ │ │ │ │ │ ├── FlipMate_icon@2x.png │ │ │ │ │ │ ├── FlipMate_icon@3x 1.png │ │ │ │ │ │ ├── FlipMate_icon_dark.png │ │ │ │ │ │ ├── FlipMate_icon_dark@2x.png │ │ │ │ │ │ ├── FlipMate_icon_dark@3x.png │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── FlipMate_Dark_icon.imageset │ │ │ │ │ │ ├── FlipMate_icon.png │ │ │ │ │ │ ├── FlipMate_icon@2x.png │ │ │ │ │ │ ├── FlipMate_icon@3x.png │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ ├── DefaultProfileImage.imageset │ │ │ │ │ │ ├── Contents.json │ │ │ │ │ │ └── DefaultProfileImage.svg │ │ │ │ │ │ ├── instruction_EN.imageset │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ └── instruction_JP.imageset │ │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── Extension │ │ │ │ │ ├── UICollectionReusableView++Extension.swift │ │ │ │ │ ├── UIImage++Extension.swift │ │ │ │ │ ├── UIView++Extension.swift │ │ │ │ │ ├── UITextField++Extension.swift │ │ │ │ │ ├── UICollectionViewCell++Extension.swift │ │ │ │ │ ├── UIImageView++Extension.swift │ │ │ │ │ └── UIViewController++Extension.swift │ │ │ │ │ ├── Base │ │ │ │ │ └── BaseViewController.swift │ │ │ │ │ ├── Buttons │ │ │ │ │ └── DoneButton.swift │ │ │ │ │ └── Font │ │ │ │ │ └── FlipMateFont.swift │ │ │ ├── .gitignore │ │ │ ├── Tests │ │ │ │ └── DesignSystemTests │ │ │ │ │ └── DesignSystemTests.swift │ │ │ └── Package.swift │ │ ├── Core │ │ │ ├── .gitignore │ │ │ ├── Sources │ │ │ │ └── Core │ │ │ │ │ ├── Constant │ │ │ │ │ └── NotificationName.swift │ │ │ │ │ ├── Error │ │ │ │ │ ├── APIError.swift │ │ │ │ │ └── NetworkError.swift │ │ │ │ │ ├── Extension │ │ │ │ │ ├── CGFloat++Extension.swift │ │ │ │ │ ├── String++Extension.swift │ │ │ │ │ ├── DateFormatter++Extension.swift │ │ │ │ │ ├── Int++Extension.swift │ │ │ │ │ └── Date++Extension.swift │ │ │ │ │ └── Logger │ │ │ │ │ └── Logger.swift │ │ │ ├── Tests │ │ │ │ └── CoreTests │ │ │ │ │ └── CoreTests.swift │ │ │ └── Package.swift │ │ ├── Domain │ │ │ ├── .gitignore │ │ │ ├── Sources │ │ │ │ └── Domain │ │ │ │ │ ├── UseCaseProtocols │ │ │ │ │ ├── Authentication │ │ │ │ │ │ ├── SignOutUseCase.swift │ │ │ │ │ │ ├── WithdrawUseCase.swift │ │ │ │ │ │ ├── GoogleLoginUseCase.swift │ │ │ │ │ │ └── AppleLoginUseCase.swift │ │ │ │ │ ├── StudingPingUseCase.swift │ │ │ │ │ ├── PatchTimeZoneUseCase.swift │ │ │ │ │ ├── Category │ │ │ │ │ │ ├── DeleteCategoryUseCase.swift │ │ │ │ │ │ ├── ReadCategoryUseCase.swift │ │ │ │ │ │ ├── CreateCategoryUseCase.swift │ │ │ │ │ │ └── UpdateCategoryUseCase.swift │ │ │ │ │ ├── Chart │ │ │ │ │ │ ├── FetchDailyChartUseCase.swift │ │ │ │ │ │ ├── FetchWeeklyChartUseCase.swift │ │ │ │ │ │ └── LoadChartUseCase.swift │ │ │ │ │ ├── GetStudyLogUseCase.swift │ │ │ │ │ ├── GetUserInfoUseCase.swift │ │ │ │ │ ├── ValidateNicknameUseCase.swift │ │ │ │ │ ├── Social │ │ │ │ │ │ ├── SetupProfileInfoUseCase.swift │ │ │ │ │ │ ├── GetFriendsUseCase.swift │ │ │ │ │ │ └── FetchFriendsUseCase.swift │ │ │ │ │ ├── Friend │ │ │ │ │ │ ├── FollowFriendUseCase.swift │ │ │ │ │ │ ├── UnfollowFriendUseCase.swift │ │ │ │ │ │ └── SearchFriendUseCase.swift │ │ │ │ │ └── Timer │ │ │ │ │ │ ├── StartTimerUseCase.swift │ │ │ │ │ │ └── FinishTimerUseCase.swift │ │ │ │ │ ├── RepositoryProtocols │ │ │ │ │ ├── ChartRepository.swift │ │ │ │ │ ├── StudyLogRepository.swift │ │ │ │ │ ├── UserInfoRepository.swift │ │ │ │ │ ├── AuthenticationRepository.swift │ │ │ │ │ ├── SocialRepository.swift │ │ │ │ │ ├── ProfileSettingsRepository.swift │ │ │ │ │ ├── CategoryRepository.swift │ │ │ │ │ ├── TimerRepository.swift │ │ │ │ │ └── FriendRepository.swift │ │ │ │ │ ├── Entities │ │ │ │ │ ├── User.swift │ │ │ │ │ ├── UserInfo.swift │ │ │ │ │ ├── FriendStatus.swift │ │ │ │ │ ├── Category.swift │ │ │ │ │ ├── FriendSearchResult.swift │ │ │ │ │ └── Friend.swift │ │ │ │ │ └── UseCaseImplementations │ │ │ │ │ ├── Authentication │ │ │ │ │ ├── DefaultSignOutUseCase.swift │ │ │ │ │ ├── DefaultWithdrawUseCase.swift │ │ │ │ │ ├── DefaultGoogleLoginUseCase.swift │ │ │ │ │ └── DefaultAppleLoginUseCase.swift │ │ │ │ │ ├── DefaultStudingPingUseCase.swift │ │ │ │ │ ├── DefaultPatchTimeZoneUseCase.swift │ │ │ │ │ ├── Category │ │ │ │ │ ├── DefaultDeleteCategoryUseCase.swift │ │ │ │ │ ├── DefaultReadCategoryUseCase.swift │ │ │ │ │ ├── DefaultCreateCategoryUseCase.swift │ │ │ │ │ └── DefaultUpdateCategoryUseCase.swift │ │ │ │ │ ├── Chart │ │ │ │ │ ├── DefaultFetchWeeklyChartUseCase.swift │ │ │ │ │ ├── DefaultLoadChartUseCase.swift │ │ │ │ │ └── DefaultFetchDailyChartUseCase.swift │ │ │ │ │ ├── DefaultGetUserInfoUseCase.swift │ │ │ │ │ ├── DefaultGetStudyLogUseCase.swift │ │ │ │ │ ├── Social │ │ │ │ │ ├── DefaultGetFriendUseCase.swift │ │ │ │ │ ├── DefaultFetchFriendUseCase.swift │ │ │ │ │ └── DefaultSetupProfileInfoUseCase.swift │ │ │ │ │ ├── Friend │ │ │ │ │ ├── DefaultUnfollowFriendUseCase.swift │ │ │ │ │ ├── DefaultFollowFriendUseCase.swift │ │ │ │ │ └── DefaultSearchFriendUseCase.swift │ │ │ │ │ ├── DefaultValidateNicknameUseCase.swift │ │ │ │ │ └── Timer │ │ │ │ │ ├── DefaultStartTimerUseCase.swift │ │ │ │ │ └── DefaultFinishTimerUseCase.swift │ │ │ ├── Tests │ │ │ │ └── DomainTests │ │ │ │ │ └── DomainTests.swift │ │ │ └── Package.swift │ │ ├── Login │ │ │ ├── .gitignore │ │ │ ├── Tests │ │ │ │ └── LoginTests │ │ │ │ │ └── LoginTests.swift │ │ │ └── Sources │ │ │ │ └── Login │ │ │ │ └── Model │ │ │ │ └── LoginType.swift │ │ ├── Network │ │ │ ├── .gitignore │ │ │ ├── Sources │ │ │ │ └── Network │ │ │ │ │ ├── Responsable.swift │ │ │ │ │ ├── HTTPMethod.swift │ │ │ │ │ ├── HTTPHeader.swift │ │ │ │ │ ├── Requestable.swift │ │ │ │ │ └── URLSessionable.swift │ │ │ ├── Tests │ │ │ │ └── NetworkTests │ │ │ │ │ ├── Models │ │ │ │ │ └── TestConstant.swift │ │ │ │ │ └── Utils │ │ │ │ │ ├── MockKeychainManager.swift │ │ │ │ │ └── MockURLProtocol.swift │ │ │ └── Package.swift │ │ ├── Social │ │ │ ├── .gitignore │ │ │ ├── Sources │ │ │ │ └── Social │ │ │ │ │ └── Model │ │ │ │ │ ├── FriendUser.swift │ │ │ │ │ ├── FriendSearchItem.swift │ │ │ │ │ ├── ProfileHeaderItem.swift │ │ │ │ │ └── UpdateFriend.swift │ │ │ ├── Tests │ │ │ │ └── SocialTests │ │ │ │ │ └── SocialTests.swift │ │ │ └── Package.swift │ │ ├── TabBar │ │ │ ├── .gitignore │ │ │ ├── Tests │ │ │ │ └── TabBarTests │ │ │ │ │ └── TabBarTests.swift │ │ │ └── Package.swift │ │ ├── Timer │ │ │ ├── .gitignore │ │ │ ├── Sources │ │ │ │ └── Timer │ │ │ │ │ └── Model │ │ │ │ │ └── DeviceOrientation.swift │ │ │ ├── Tests │ │ │ │ └── TimerTests │ │ │ │ │ └── TimerTests.swift │ │ │ └── Package.swift │ │ ├── FlipMate.entitlements │ │ ├── Selection.ahap │ │ └── Resources │ │ │ └── Info.plist │ ├── FlipMate.xcodeproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── .swiftlint.yml ├── FMImageProvider │ ├── .gitignore │ ├── .swiftpm │ │ └── xcode │ │ │ └── package.xcworkspace │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Sources │ │ └── FMImageProvider │ │ │ ├── Utils │ │ │ └── Logger.swift │ │ │ └── Protocols │ │ │ ├── MemoryCacheable.swift │ │ │ ├── DiskCacheable.swift │ │ │ └── ImageDownloadable.swift │ ├── Tests │ │ └── FMImageProviderTests │ │ │ ├── Utils │ │ │ └── Logger.swift │ │ │ └── Model │ │ │ └── Dummy.swift │ └── Package.swift └── FlipMate.xctestplan ├── BE ├── test │ ├── mock-table │ │ ├── redis-study-logs.json │ │ ├── mates.json │ │ ├── users.json │ │ ├── study-logs.json │ │ └── categories.json │ ├── mock-service │ │ ├── mock-config-service.ts │ │ ├── mock-user-service.ts │ │ ├── mock-redis-service.ts │ │ └── mock-study-logs-service.ts │ ├── jest-e2e.json │ ├── app.e2e-spec.ts │ ├── load-performance │ │ └── loadtest-social.js │ └── mock-repo │ │ └── mock-categories-repo.ts ├── .prettierrc ├── src │ ├── common │ │ ├── const │ │ │ ├── service-var.const.ts │ │ │ └── env-keys.const.ts │ │ ├── config │ │ │ ├── static.config.ts │ │ │ ├── swagger.config.ts │ │ │ ├── jwt.config.ts │ │ │ ├── multer.config.ts │ │ │ ├── typeorm.config.ts │ │ │ └── logging.config.ts │ │ ├── middleware │ │ │ └── logging.middleware.ts │ │ ├── response.dto.ts │ │ ├── utils │ │ │ └── utils.ts │ │ ├── redis.service.ts │ │ ├── exception-filter │ │ │ └── http-exception-filter.ts │ │ ├── interceptor │ │ │ └── logging.interceptor.ts │ │ └── s3.service.ts │ ├── users │ │ ├── const │ │ │ └── auth-type.const.ts │ │ ├── dto │ │ │ ├── response │ │ │ │ ├── nickname-validation.dto.ts │ │ │ │ └── user-profile.dto.ts │ │ │ ├── create-user.dto.ts │ │ │ └── update-user.dto.ts │ │ ├── interface │ │ │ └── greeneye.interface.ts │ │ ├── users.module.ts │ │ └── decorator │ │ │ └── user.decorator.ts │ ├── app.service.ts │ ├── mates │ │ ├── dto │ │ │ ├── response │ │ │ │ ├── status-message.dto.ts │ │ │ │ ├── mates.dto.ts │ │ │ │ ├── mates-info.dto.ts │ │ │ │ └── follower-info.dto.ts │ │ │ └── request │ │ │ │ └── pagination-query.dto.ts │ │ ├── mates.module.ts │ │ └── mates.entity.ts │ ├── categories │ │ ├── dto │ │ │ ├── request │ │ │ │ ├── create-categories.dto.ts │ │ │ │ └── update-categories.dto.ts │ │ │ └── response │ │ │ │ └── category.dto.ts │ │ ├── categories.module.ts │ │ └── categories.entity.ts │ ├── app.controller.ts │ ├── admin │ │ └── admin.module.ts │ ├── study-logs │ │ ├── dto │ │ │ ├── response │ │ │ │ ├── today-logs.dto.ts │ │ │ │ ├── daily-stat.dto.ts │ │ │ │ ├── weekly-stats.dto.ts │ │ │ │ └── study-logs.dto.ts │ │ │ └── request │ │ │ │ └── create-study-logs.dto.ts │ │ └── study-logs.module.ts │ ├── auth │ │ ├── dto │ │ │ └── response │ │ │ │ ├── update-info.dto.ts │ │ │ │ └── get-info.dto.ts │ │ ├── google.strategy.ts │ │ ├── auth.service.spec.ts │ │ └── auth.module.ts │ ├── heartbeat │ │ ├── heartbeat.module.ts │ │ └── heartbeat.controller.ts │ └── app.controller.spec.ts ├── tsconfig.build.json ├── nest-cli.json ├── .gitignore ├── tsconfig.json └── .eslintrc.js ├── .DS_Store └── .github ├── pull_request_template.md ├── ISSUE_TEMPLATE ├── be-template.md ├── 버그-리포트.md └── ios-feature-template.md └── workflows ├── be-main-workflow.yaml └── be-pre-production-workflow.yaml /iOS/FlipMate/Feature/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /BE/test/mock-table/redis-study-logs.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Application/DIContainer/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Presentation/ChartScene/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /BE/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/.DS_Store -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # 이슈번호-작업이름 2 | 3 | ## 완료된 기능 4 | 5 | 6 | ## 고민과 해결과정 7 | -------------------------------------------------------------------------------- /BE/src/common/const/service-var.const.ts: -------------------------------------------------------------------------------- 1 | export const MATES_MAXIMUM = 10; 2 | export const CATEGORIES_MAXIMUM = 10; 3 | -------------------------------------------------------------------------------- /BE/src/users/const/auth-type.const.ts: -------------------------------------------------------------------------------- 1 | export enum AuthTypeEnum { 2 | GOOGLE = 'google', 3 | APPLE = 'apple', 4 | } 5 | -------------------------------------------------------------------------------- /BE/test/mock-table/mates.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "follower_id": 1, 5 | "following_id": 2 6 | } 7 | ] -------------------------------------------------------------------------------- /BE/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /BE/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/be-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: BE Template 3 | about: BE Template 4 | title: '' 5 | labels: BE 6 | assignees: victolee0, yeongbinim 7 | 8 | --- 9 | 10 | ## Why 11 | 12 | ## Todo 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/버그-리포트.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 버그 리포트 3 | about: 버그 리포트를 위한 탬플릿입니다 4 | title: '' 5 | labels: "\U0001F41E Bug" 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 버그 발생 환경 11 | 12 | ## 버그 발생 내용 13 | -------------------------------------------------------------------------------- /BE/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BE/src/common/config/static.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | export const staticConfig = { 4 | rootPath: path.join(__dirname, '../../../../..', 'apps'), 5 | serveRoot: '/', 6 | renderPath: '/', 7 | }; 8 | -------------------------------------------------------------------------------- /BE/test/mock-service/mock-config-service.ts: -------------------------------------------------------------------------------- 1 | export class MockConfigService { 2 | private ENV = { 3 | CDN_ENDPOINT: 'http://cdn.com', 4 | }; 5 | get(key: string) { 6 | return this.ENV[key]; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /iOS/FMImageProvider/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Chart/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Category/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/MyPage/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Service/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Login/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Network/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Social/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/TabBar/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Timer/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BE/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Social/Sources/Social/Model/FriendUser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 6/2/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum Section: CaseIterable { 11 | case main 12 | } 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ios-feature-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: iOS Feature Template 3 | about: iOS Feature Template 4 | title: '' 5 | labels: iOS 6 | assignees: ericKwon95, leemhyungyu, nemanjabenkovic 7 | 8 | --- 9 | 10 | ## Why 11 | 12 | ## Todo 13 | - 14 | - 15 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Application/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 11/9/23. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Network/Sources/Network/Responsable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Responsable.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/15/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol Responsable { 11 | associatedtype Response: Decodable 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png -------------------------------------------------------------------------------- /BE/src/common/config/swagger.config.ts: -------------------------------------------------------------------------------- 1 | import { DocumentBuilder } from '@nestjs/swagger'; 2 | 3 | export const swaggerConfig = new DocumentBuilder() 4 | .setTitle('StudyLog API') 5 | .setDescription('StudyLog 애플리케이션 API 문서') 6 | .setVersion('2.0') 7 | .addBearerAuth() 8 | .build(); 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /BE/src/users/dto/response/nickname-validation.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class NicknameValidationDto { 4 | @ApiProperty({ 5 | type: 'boolean', 6 | example: true, 7 | description: '가능한지 여부', 8 | }) 9 | is_unique: boolean; 10 | } 11 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Authentication/SignOutUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignOutUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol SignOutUseCase { 11 | func signOut() 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/StudingPingUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudingPingUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol StudingPingUseCase { 11 | func studingPing() async throws 12 | } 13 | -------------------------------------------------------------------------------- /BE/src/users/dto/response/user-profile.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class UserProfileDto { 4 | @ApiProperty({ 5 | type: 'string', 6 | example: 'https://imgurl.com/path/file.png', 7 | description: '유저 프로필 사진', 8 | }) 9 | image_url: string; 10 | } 11 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Sources/Core/Constant/NotificationName.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationName.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/17/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum NotificationName { 11 | public static let signOut = NSNotification.Name("signOut") 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon 1.png -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon@2x.png -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Authentication/WithdrawUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WithdrawUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol WithdrawUseCase { 11 | func withdraw() async throws 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_Dark_icon.imageset/FlipMate_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_Dark_icon.imageset/FlipMate_icon.png -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon@3x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon@3x 1.png -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon_dark.png -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/TimerScene/TimeZoneRequestDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeZoneRequestDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/12/13. 6 | // 7 | 8 | import Foundation 9 | 10 | struct TimeZoneRequestDTO: Encodable { 11 | let timezone: String 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UICollectionReusableView++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionReusableView++Extension.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/20. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UICollectionReusableView: ReusableView {} 11 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_Dark_icon.imageset/FlipMate_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_Dark_icon.imageset/FlipMate_icon@2x.png -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_Dark_icon.imageset/FlipMate_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_Dark_icon.imageset/FlipMate_icon@3x.png -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon_dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon_dark@2x.png -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon_dark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS06-FlipMate/HEAD/iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/FlipMate_icon_dark@3x.png -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/PatchTimeZoneUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PatchTimeZoneUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol PatchTimeZoneUseCase { 11 | func patchTimeZone(date: Date) async throws 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/StatusResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatusResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 11/22/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct StatusResponseDTO: Decodable { 11 | let statusCode: Int 12 | let message: String 13 | } 14 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Category/DeleteCategoryUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeleteCategoryUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol DeleteCategoryUseCase { 11 | func deleteCategory(of id: Int) async throws 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Category/ReadCategoryUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReadCategoryUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol ReadCategoryUseCase { 11 | func readCategory() async throws -> [StudyCategory] 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Sources/Core/Error/APIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIError.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/14/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum APIError: Error { 11 | case duplicatedCategoryName 12 | case duplicatedNickName 13 | case imageNotSafe 14 | case unknown 15 | } 16 | -------------------------------------------------------------------------------- /BE/src/common/config/jwt.config.ts: -------------------------------------------------------------------------------- 1 | import { ConfigService } from '@nestjs/config'; 2 | import { ENV } from '../const/env-keys.const'; 3 | 4 | export const jwtConfig = (configService: ConfigService) => ({ 5 | secret: configService.get(ENV.JWT_SECRET), 6 | signOptions: { expiresIn: configService.get(ENV.JWT_EXPIRES_IN) }, 7 | }); 8 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/DefaultProfileImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "DefaultProfileImage.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Authentication/GoogleLoginUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GoogleLoginUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol GoogleLoginUseCase { 11 | func googleLogin(accessToken: String) async throws -> User 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Network/Sources/Network/HTTPMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPMethod.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/15/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum HTTPMethod: String { 11 | case get = "GET" 12 | case post = "POST" 13 | case delete = "DELETE" 14 | case patch = "PATCH" 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FMImageProvider/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /iOS/FMImageProvider/Sources/FMImageProvider/Utils/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 3/11/24. 6 | // 7 | 8 | import Foundation 9 | import OSLog 10 | 11 | enum FMLogger { 12 | static let general = Logger(subsystem: Bundle.main.bundleIdentifier ?? "FMImageProvider", category: "FMImageProvider") 13 | } 14 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UIImage++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage++Extension.swift 3 | // 4 | // 5 | // Created by 임현규 on 5/17/24. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIImage { 11 | public static let profileImage = UIImage(named: "DefaultProfileImage", in: Bundle.module, with: nil) 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Authentication/AppleLoginUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppleLoginUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol AppleLoginUseCase { 11 | func appleLogin(accessToken: String, userID: String) async throws -> User 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Category/CreateCategoryUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CreateCategoryUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol CreateCategoryUseCase { 11 | func createCategory(name: String, colorCode: String) async throws -> Int 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Chart/FetchDailyChartUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FetchDailyChartUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol FetchDailyChartUseCase { 11 | func fetchDailyChartLog(at date: Date) async throws -> CategoryChartLog 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Chart/FetchWeeklyChartUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FetchWeeklyChartUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol FetchWeeklyChartUseCase { 11 | func fetchWeeklyChartLog(at date: Date) async throws -> WeeklyChartLog 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/FlipMate.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.applesignin 6 | 7 | Default 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /iOS/FMImageProvider/Sources/FMImageProvider/Protocols/MemoryCacheable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageCacheable.swift 3 | // 4 | // 5 | // Created by 권승용 on 1/22/24. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol MemoryCacheable { 11 | func save(key url: String, imageData: Data) 12 | func load(key url: String) throws -> Data 13 | func removeAll() 14 | } 15 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/GetStudyLogUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetStudyLogUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol GetStudyLogUseCase { 14 | func getStudyLog() -> AnyPublisher 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/GetUserInfoUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetUserInfoUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol GetUserInfoUseCase { 14 | func getUserInfo() -> AnyPublisher 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Sources/Core/Extension/CGFloat++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGFloat++Extension.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/14/24. 6 | // 7 | 8 | import Foundation 9 | 10 | extension CGFloat { 11 | /// CGFloat 값을 라디안 값으로 변환하여 리턴합니다. 12 | public func toRadian() -> CGFloat { 13 | return self * 2 * CGFloat.pi 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Category/UpdateCategoryUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpdateCategoryUseCsae.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol UpdateCategoryUseCsae { 11 | func updateCategory(of id: Int, newName: String, newColorCode: String) async throws 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/ValidateNicknameUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ValidateNicknameUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | import Core 11 | 12 | public protocol ValidateNicknameUseCase { 13 | func isNickNameValid(_ nickName: String) -> NickNameValidationState 14 | } 15 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Social/SetupProfileInfoUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SetupProfileInfoUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol SetupProfileInfoUseCase { 11 | func setupProfileInfo(nickName: String, profileImageData: Data) async throws -> UserInfo 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "AppIcon.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Chart/LoadChartUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadChartUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol LoadChartUseCase { 14 | func loadChart(at id: Int) -> AnyPublisher 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Social/GetFriendsUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetFriendsUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol GetFriendsUseCase { 14 | func getMyFriend(date: Date) -> AnyPublisher<[Friend], NetworkError> 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/RepositoryProtocols/ChartRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartRepository.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol ChartRepository { 11 | func fetchDailyLog(date: Date) async throws -> CategoryChartLog 12 | func fetchWeeklyLog() async throws -> WeeklyChartLog 13 | } 14 | -------------------------------------------------------------------------------- /iOS/FMImageProvider/Sources/FMImageProvider/Protocols/DiskCacheable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DiskCacheable.swift 3 | // 4 | // 5 | // Created by 권승용 on 1/22/24. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol DiskCacheable { 11 | func save(key url: String, imageData: Data) async throws 12 | func load(key url: String) async throws -> Data? 13 | func removeAll() async throws 14 | } 15 | -------------------------------------------------------------------------------- /iOS/FMImageProvider/Sources/FMImageProvider/Protocols/ImageDownloadable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageDownloadable.swift 3 | // 4 | // 5 | // Created by 권승용 on 1/23/24. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol ImageDownloadable { 11 | func fetchImage(from url: URL, completion: @escaping (Result) -> ()) 12 | func fetchImage(from url: URL) async throws -> Data 13 | } 14 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Friend/FollowFriendUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FollowFriendUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol FollowFriendUseCase { 14 | func follow(at nickname: String) -> AnyPublisher 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Friend/UnfollowFriendUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnfollowFriendUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol UnfollowFriendUseCase { 14 | func unfollow(at id: Int) -> AnyPublisher 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FMImageProvider/Tests/FMImageProviderTests/Utils/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 3/19/24. 6 | // 7 | 8 | import Foundation 9 | import OSLog 10 | @testable import FMImageProvider 11 | 12 | extension FMLogger { 13 | static let test = Logger(subsystem: Bundle.main.bundleIdentifier ?? "FMImageProvider", category: "FMImageProviderTests") 14 | } 15 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Sources/Core/Extension/String++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String++Extension.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/14/24. 6 | // 7 | 8 | import Foundation 9 | 10 | extension String { 11 | public func toDate(_ dateFormat: Date.FMDateFormmat) -> Date? { 12 | return DateFormatter.FMDateFormat(dateFormat: dateFormat).date(from: self) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Social/FetchFriendsUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FetchFriendsUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol FetchFriendsUseCase { 14 | func fetchMyFriend(date: Date) -> AnyPublisher<[FriendStatus], NetworkError> 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Friend/SearchFriendUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchFriendUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol SearchFriendUseCase { 14 | func search(at nickname: String) -> AnyPublisher 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Timer/StartTimerUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartTimerUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol StartTimerUseCase { 14 | func startTimer(startTime: Date, categoryId: Int?) -> AnyPublisher 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Network/Sources/Network/HTTPHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPHeader.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/15/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct HTTPHeader { 11 | var value: String 12 | var field: String 13 | 14 | public init(value: String, field: String) { 15 | self.value = value 16 | self.field = field 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/RepositoryProtocols/StudyLogRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudyLogRepository.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol StudyLogRepository { 14 | func getUserInfo() -> AnyPublisher 15 | func studingPing() async throws 16 | } 17 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseProtocols/Timer/FinishTimerUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FinishTimerUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol FinishTimerUseCase { 14 | func finishTimer(endTime: Date, learningTime: Int, categoryId: Int?) -> AnyPublisher 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Timer/Sources/Timer/Model/DeviceOrientation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceOrientation.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/30/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum DeviceOrientation: Int { 11 | case unknown 12 | case portrait 13 | case portraitUpsideDown 14 | case landscapeLeft 15 | case landscapeRight 16 | case faceUp 17 | case faceDown 18 | } 19 | -------------------------------------------------------------------------------- /BE/src/mates/dto/response/status-message.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class StatusMessageDto { 4 | @ApiProperty({ 5 | type: 'number', 6 | example: 200, 7 | description: '상태 코드', 8 | }) 9 | statusCode: number; 10 | 11 | @ApiProperty({ 12 | type: 'string', 13 | example: '성공적으로 삭제되었습니다.', 14 | description: '메시지', 15 | }) 16 | message: string; 17 | } 18 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/RepositoryProtocols/UserInfoRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfoRepository.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol UserInfoRepository { 14 | func getUserInfo() -> AnyPublisher 15 | func patchTimeZone(date: Date) async throws 16 | } 17 | -------------------------------------------------------------------------------- /BE/src/categories/dto/request/create-categories.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class CategoryCreateDto { 4 | @ApiProperty({ 5 | type: 'string', 6 | example: '백준', 7 | description: '카테고리 이름', 8 | }) 9 | name: string; 10 | 11 | @ApiProperty({ 12 | type: 'string', 13 | example: 'FFFFFFFF', 14 | description: '카테고리 색상', 15 | }) 16 | color_code: string; 17 | } 18 | -------------------------------------------------------------------------------- /BE/src/categories/dto/request/update-categories.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class CategoryUpdateDto { 4 | @ApiProperty({ 5 | type: 'string', 6 | example: '백준', 7 | description: '카테고리 이름', 8 | }) 9 | name: string; 10 | 11 | @ApiProperty({ 12 | type: 'string', 13 | example: 'FFFFFFFF', 14 | description: '카테고리 색상', 15 | }) 16 | color_code: string; 17 | } 18 | -------------------------------------------------------------------------------- /BE/test/mock-service/mock-user-service.ts: -------------------------------------------------------------------------------- 1 | import { UsersModel } from 'src/users/entity/users.entity'; 2 | import usersData from '../mock-table/users.json'; 3 | 4 | export class MockUsersService { 5 | private data: UsersModel[] = usersData as UsersModel[]; 6 | findUserById(user_id: number): Promise { 7 | const user = this.data.find((user) => user.id === user_id); 8 | return Promise.resolve(user); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/SocialScene/FriendUnfollowRequestDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FriendUnfollowRequestDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 12/4/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct FriendUnfollowRequestDTO: Encodable { 11 | let id: Int 12 | 13 | private enum CodingKeys: String, CodingKey { 14 | case id = "following_id" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BE/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | import { ApiExcludeEndpoint } from '@nestjs/swagger'; 4 | 5 | @Controller() 6 | export class AppController { 7 | constructor(private readonly appService: AppService) {} 8 | 9 | @Get('') 10 | @ApiExcludeEndpoint() 11 | getHello(): string { 12 | return this.appService.getHello(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/StatusResponseWithErrorDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatusResponseWithErrorDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 11/28/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct StatusResponseWithErrorDTO: Decodable { 11 | let statusCode: Int 12 | let message: String 13 | let error: String 14 | let path: String 15 | let timestamp: String 16 | } 17 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/LoginScene/GoogleAuthRequestDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GoogleLoginRequestDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 11/23/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct GoogleAuthRequestDTO: Encodable { 11 | let accessToken: String 12 | 13 | private enum CodingKeys: String, CodingKey { 14 | case accessToken = "access_token" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/LoginScene/AppleAuthRequestDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppleAuthRequestDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 12/14/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct AppleAuthRequestDTO: Encodable { 11 | let identityToken: String 12 | 13 | private enum CodingKeys: String, CodingKey { 14 | case identityToken = "identity_token" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/Entities/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // User.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct User { 11 | public let isMember: Bool 12 | public let accessToken: String 13 | 14 | public init(isMember: Bool, accessToken: String) { 15 | self.isMember = isMember 16 | self.accessToken = accessToken 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BE/src/admin/admin.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AdminController } from './admin.controller'; 3 | import { AuthModule } from 'src/auth/auth.module'; 4 | import { UsersModule } from 'src/users/users.module'; 5 | import { MatesModule } from 'src/mates/mates.module'; 6 | 7 | @Module({ 8 | imports: [AuthModule, UsersModule, MatesModule], 9 | controllers: [AdminController], 10 | }) 11 | export class AdminModule {} 12 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/SocialScene/FriendFollowReqeustDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FriendFollowReqeustDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/29. 6 | // 7 | 8 | import Foundation 9 | 10 | struct FriendFollowReqeustDTO: Encodable { 11 | let nickname: String 12 | 13 | private enum CodingKeys: String, CodingKey { 14 | case nickname = "following_nickname" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Tests/CoreTests/CoreTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Core 3 | 4 | final class CoreTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/LoginScene/NickNameValidationResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NickNameValidationResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 11/28/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct NickNameValidationResponseDTO: Decodable { 11 | let isUnique: Bool 12 | 13 | private enum CodingKeys: String, CodingKey { 14 | case isUnique = "is_unique" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Tests/DataTests/DataTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Data 3 | 4 | final class DataTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Chart/Tests/ChartTests/ChartTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Chart 3 | 4 | final class ChartTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/RepositoryProtocols/AuthenticationRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthenticationRepository.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol AuthenticationRepository { 11 | func googleLogin(with accessToken: String) async throws -> User 12 | func appleLogin(with accessToken: String) async throws -> User 13 | func withdraw() async throws 14 | } 15 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/RepositoryProtocols/SocialRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SocialRepository.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol SocialRepository { 14 | func getMyFriend(date: Date) -> AnyPublisher<[Friend], NetworkError> 15 | func fetchMyFriend(date: Date) -> AnyPublisher<[FriendStatus], NetworkError> 16 | } 17 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Login/Tests/LoginTests/LoginTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Login 3 | 4 | final class LoginTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Timer/Tests/TimerTests/TimerTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Timer 3 | 4 | final class TimerTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/MyPage/Tests/MyPageTests/MyPageTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import MyPage 3 | 4 | final class MyPageTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/RepositoryProtocols/ProfileSettingsRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileSettingsRepository.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol ProfileSettingsRepository { 11 | func checkIfNickNameIsDuplicated(_ nickName: String) async throws -> Bool 12 | func setupNewProfileInfo(nickName: String, profileImageData: Data) async throws -> UserInfo 13 | } 14 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Tests/DomainTests/DomainTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Domain 3 | 4 | final class DomainTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Social/Tests/SocialTests/SocialTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Social 3 | 4 | final class SocialTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/TabBar/Tests/TabBarTests/TabBarTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import TabBar 3 | 4 | final class TabBarTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BE/src/mates/dto/request/pagination-query.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { Type } from 'class-transformer'; 3 | import { IsNumber, Min } from 'class-validator'; 4 | 5 | export class PaginationQueryDto { 6 | @ApiProperty({ 7 | type: 'number', 8 | example: 1, 9 | description: '현재 page 위치 (default: 1)', 10 | }) 11 | @Type(() => Number) // Add this line 12 | @IsNumber() 13 | @Min(1) 14 | page: number = 1; 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Service/Tests/ServiceTests/ServiceTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Service 3 | 4 | final class ServiceTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/SocialScene/SocialDetailRequestDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FriendChartRequestDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 11/30/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct SocialDetailRequestDTO: Encodable { 11 | let followingID: Int 12 | let date: String 13 | 14 | private enum CodingKeys: String, CodingKey { 15 | case followingID = "following_id" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /iOS/FMImageProvider/Tests/FMImageProviderTests/Model/Dummy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 3/15/24. 6 | // 7 | 8 | import Foundation 9 | 10 | enum ImageData { 11 | static let dummy = Data(count: 10) 12 | } 13 | 14 | enum CacheKey { 15 | static let dummy1 = "https://dummyURL" 16 | static let dummy2 = "https://dummyURL2" 17 | static let dummy3 = "https://dummyURL3" 18 | static let dummy4 = "https://dummyURL4" 19 | } 20 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Category/Tests/CategoryTests/CategoryTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Category 3 | 4 | final class CategoryTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/Category/CategoryRequestDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CategoryUpdateDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 11/22/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct CategoryRequestDTO: Encodable { 11 | let name: String 12 | let colorCode: String 13 | 14 | private enum CodingKeys: String, CodingKey { 15 | case name 16 | case colorCode = "color_code" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Sources/Core/Extension/DateFormatter++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateFormatter++Extension.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/14/24. 6 | // 7 | 8 | import Foundation 9 | 10 | extension DateFormatter { 11 | public static func FMDateFormat(dateFormat: Date.FMDateFormmat) -> DateFormatter { 12 | let formatter = DateFormatter() 13 | formatter.dateFormat = dateFormat.rawValue 14 | return formatter 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Network/Sources/Network/Requestable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Requestable.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/15/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol Requestable { 11 | var baseURL: String { get } 12 | var path: String { get } 13 | var method: HTTPMethod { get } 14 | var data: Data? { get } 15 | var headers: [HTTPHeader]? { get } 16 | func makeURLRequest(with token: String?) throws -> URLRequest 17 | } 18 | -------------------------------------------------------------------------------- /BE/src/common/middleware/logging.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestMiddleware } from '@nestjs/common'; 2 | import { NextFunction } from 'express'; 3 | import { v4 as uuid } from 'uuid'; 4 | 5 | @Injectable() 6 | export class LoggingMiddleware implements NestMiddleware { 7 | use( 8 | req: Request & { now: number; id: string }, 9 | res: Response, 10 | next: NextFunction, 11 | ) { 12 | req.now = Date.now(); 13 | req.id = uuid(); 14 | next(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BE/src/study-logs/dto/response/today-logs.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { CategoryDto } from 'src/categories/dto/response/category.dto'; 3 | 4 | export class TodayLogsDto { 5 | @ApiProperty({ 6 | type: 'number', 7 | example: 10000, 8 | description: '해당 날짜의 총 학습 시간', 9 | }) 10 | total_time: number; 11 | 12 | @ApiProperty({ 13 | type: Array(CategoryDto), 14 | description: '학습 한 카테고리들', 15 | }) 16 | categories: object[]; 17 | } 18 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/SocialScene/FreindStatusResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FreindStatusResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/12/02. 6 | // 7 | 8 | import Foundation 9 | 10 | struct FriendStatusResponseDTO: Decodable { 11 | let id: Int 12 | let startedTime: String? 13 | 14 | private enum CodingKeys: String, CodingKey { 15 | case id 16 | case startedTime = "started_at" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Tests/DesignSystemTests/DesignSystemTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import DesignSystem 3 | 4 | final class DesignSystemTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/LoginScene/AuthResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GoogleLoginResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 11/23/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct AuthResponseDTO: Decodable { 11 | let isMember: Bool 12 | let accessToken: String 13 | 14 | private enum CodingKeys: String, CodingKey { 15 | case isMember = "is_member" 16 | case accessToken = "access_token" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Authentication/DefaultSignOutUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultSignOutUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | import Core 11 | 12 | public struct DefaultSignOutUseCase: SignOutUseCase { 13 | 14 | public init() {} 15 | 16 | public func signOut() { 17 | NotificationCenter.default.post(name: NotificationName.signOut, object: nil) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/SocialScene/UserProfileResposeDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserProfileResposeDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/29. 6 | // 7 | 8 | import Foundation 9 | 10 | struct UserProfileResposeDTO: Decodable { 11 | let statusCode: Int 12 | let profileImageURL: String? 13 | 14 | private enum CodingKeys: String, CodingKey { 15 | case statusCode 16 | case profileImageURL = "image_url" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UIView++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView++Extension.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/14. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | public func setShadow() { 12 | self.layer.shadowColor = UIColor.black.cgColor 13 | self.layer.shadowOffset = CGSize(width: 3.0, height: 3.0) 14 | self.layer.shadowOpacity = 0.3 15 | self.layer.shadowRadius = 2.0 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Chart/Sources/Chart/Model/WeeklySection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 6/2/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum WeeklySection: Hashable { 11 | case section([WeeklySectionItem]) 12 | } 13 | 14 | public enum WeeklySectionItem: Hashable { 15 | case dateCell(String) 16 | 17 | var date: String { 18 | switch self { 19 | case .dateCell(let date): 20 | return date 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BE/src/users/interface/greeneye.interface.ts: -------------------------------------------------------------------------------- 1 | export interface GreenEyeResponse { 2 | version: string; 3 | requestId: string; 4 | timestamp: number; 5 | images: [ 6 | { 7 | message: string; 8 | name: string; 9 | result: { 10 | adult: { confidence: number }; 11 | normal: { confidence: number }; 12 | porn: { confidence: number }; 13 | sexy: { confidence: number }; 14 | }; 15 | latency: number; 16 | confidence: number; 17 | }, 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /BE/test/mock-table/users.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "nickname": "어린콩", 5 | "auth_type": "google", 6 | "email": "yeim.dev@gmail.com", 7 | "image_url": "image.png" 8 | }, 9 | { 10 | "id": 2, 11 | "nickname": "어린콩2", 12 | "auth_type": "google", 13 | "email": "yeim.de@gmail.com", 14 | "image_url": null 15 | }, 16 | { 17 | "id": 3, 18 | "nickname": "어린콩3", 19 | "auth_type": "google", 20 | "email": "yeim.d@gmail.com", 21 | "image_url": null 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Sources/Core/Extension/Int++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int++Extension.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/14/24. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Int { 11 | /// 정수를 통해 HH:mm:ss 형식의 문자열로 만들어 리턴합니다. 12 | public func secondsToStringTime() -> String { 13 | let hour = self / 3600 14 | let minute = (self % 3600) / 60 15 | let second = self % 60 16 | return String(format: "%02d:%02d:%02d", hour, minute, second) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UITextField++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField++Extension.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 11/26/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UITextField { 11 | public func addLeftPadding(width: Int) { 12 | let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: Int(self.frame.height))) 13 | self.leftView = paddingView 14 | self.leftViewMode = ViewMode.always 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/LoginScene/UserInfoResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfoResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/12/04. 6 | // 7 | 8 | import Foundation 9 | 10 | struct UserInfoResponseDTO: Decodable { 11 | let nickname: String 12 | let email: String 13 | let imageURL: String? 14 | 15 | private enum CodingKeys: String, CodingKey { 16 | case nickname 17 | case email 18 | case imageURL = "image_url" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/DarkBlue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.362", 9 | "green" : "0.205", 10 | "red" : "0.187" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/Gray2.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.553", 9 | "green" : "0.553", 10 | "red" : "0.553" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/Gray4.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.973", 9 | "green" : "0.966", 10 | "red" : "0.965" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/Entities/UserInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfo.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct UserInfo { 11 | public let name: String 12 | public let profileImageURL: String? 13 | public let email: String 14 | 15 | public init(name: String, profileImageURL: String?, email: String) { 16 | self.name = name 17 | self.profileImageURL = profileImageURL 18 | self.email = email 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/LoginScene/SignUpResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignUpResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 11/28/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct SignUpResponseDTO: Decodable { 11 | let nickName: String 12 | let email: String 13 | let imageURL: String? 14 | 15 | private enum CodingKeys: String, CodingKey { 16 | case nickName = "nickname" 17 | case email 18 | case imageURL = "image_url" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/Gray5.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.778", 9 | "green" : "0.774", 10 | "red" : "0.771" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/Entities/FriendStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FriendStatus.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct FriendStatus: Hashable { 11 | public let id: Int 12 | public let totalTime: Int 13 | public let startedTime: String? 14 | 15 | public init(id: Int, totalTime: Int, startedTime: String?) { 16 | self.id = id 17 | self.totalTime = totalTime 18 | self.startedTime = startedTime 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/RepositoryProtocols/CategoryRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CategoryRepository.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol CategoryRepository { 11 | func createCategory(name: String, colorCode: String) async throws -> Int 12 | func readCategories() async throws -> [StudyCategory] 13 | func updateCategory(id: Int, newName: String, newColorCode: String) async throws 14 | func deleteCategory(id: Int) async throws 15 | } 16 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Network/Tests/NetworkTests/Models/TestConstant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestConstant.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/16/24. 6 | // 7 | 8 | import Foundation 9 | 10 | enum TestConstant { 11 | static let url = URL(string:"https://example.com")! 12 | static let dummyData = TestCodableType(title: "title", body: "body") 13 | static let token = "token" 14 | static let path = "path" 15 | } 16 | 17 | struct TestCodableType: Codable, Equatable { 18 | let title: String 19 | let body: String 20 | } 21 | -------------------------------------------------------------------------------- /BE/src/common/response.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class ResponseDto { 4 | @ApiProperty({ 5 | type: 'number', 6 | example: 200, 7 | description: '상태 코드', 8 | }) 9 | statusCode: number; 10 | 11 | @ApiProperty({ 12 | type: 'string', 13 | example: '요청이 정상적으로 처리되었습니다.', 14 | description: '메세지', 15 | }) 16 | message: string; 17 | 18 | constructor(statusCode: number, message: string) { 19 | this.statusCode = statusCode; 20 | this.message = message; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /BE/src/common/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | export function transformDate(date: Date): string { 4 | const year = date.getFullYear(); 5 | const month = date.getMonth() + 1; 6 | const day = date.getDate(); 7 | 8 | const monthStr = month < 10 ? '0' + month : month; 9 | const dayStr = day < 10 ? '0' + day : day; 10 | 11 | return `${year}-${monthStr}-${dayStr}`; 12 | } 13 | 14 | export function getImageUrl(prefix: string, image_url: string): string { 15 | return image_url ? path.join(prefix, image_url) : null; 16 | } 17 | -------------------------------------------------------------------------------- /BE/test/mock-service/mock-redis-service.ts: -------------------------------------------------------------------------------- 1 | export class MockRedisService { 2 | private redis: Map = new Map(); 3 | constructor() {} 4 | hset(key: string, field: string, value: string) { 5 | const data = {}; 6 | data[field] = value; 7 | this.redis.set(key, data); 8 | } 9 | 10 | hget(key: string, field: string): Promise { 11 | return Promise.resolve(this.redis.get(key)[field]); 12 | } 13 | 14 | async del(key: string): Promise { 15 | await this.redis.delete(key); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/Category/CategoryResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CategoryResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 11/22/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct CategoryResponseDTO: Decodable { 11 | let categoryID: Int 12 | let name: String 13 | let colorCode: String 14 | 15 | private enum CodingKeys: String, CodingKey { 16 | case categoryID = "category_id" 17 | case name 18 | case colorCode = "color_code" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BE/src/mates/dto/response/mates.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class MatesDto { 4 | @ApiProperty({ 5 | type: 'number', 6 | example: 1, 7 | description: '친구 관계 id', 8 | }) 9 | id: number; 10 | 11 | @ApiProperty({ 12 | type: 'number', 13 | example: 1, 14 | description: '구독의 주체 id (1이 2를 구독중)', 15 | }) 16 | follower_id: number; 17 | 18 | @ApiProperty({ 19 | type: 'number', 20 | example: 2, 21 | description: '구독 중인 친구 id', 22 | }) 23 | following_id: number; 24 | } 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/TabBarIconUnSelected.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.553", 9 | "green" : "0.553", 10 | "red" : "0.553" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BE/src/categories/dto/response/category.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class CategoryDto { 4 | @ApiProperty({ 5 | type: 'number', 6 | example: '1', 7 | description: '카테고리 id', 8 | }) 9 | category_id: number; 10 | 11 | @ApiProperty({ 12 | type: 'string', 13 | example: '백준', 14 | description: '카테고리 이름', 15 | }) 16 | name: string; 17 | 18 | @ApiProperty({ 19 | type: 'string', 20 | example: 'FFFFFFFF', 21 | description: '카테고리 색상', 22 | }) 23 | color_code: string; 24 | } 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Social/Sources/Social/Model/FriendSearchItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 6/2/24. 6 | // 7 | 8 | import Foundation 9 | import Domain 10 | 11 | public struct FreindSeacrhItem { 12 | let nickname: String 13 | let iamgeURL: String? 14 | let status: FriendSearchStatus 15 | 16 | public init(nickname: String, iamgeURL: String?, status: FriendSearchStatus) { 17 | self.nickname = nickname 18 | self.iamgeURL = iamgeURL 19 | self.status = status 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /BE/src/mates/dto/response/mates-info.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class MatesInfoDto { 4 | @ApiProperty({ 5 | type: 'number', 6 | example: 1, 7 | description: '친구 관계 id', 8 | }) 9 | id: number; 10 | 11 | @ApiProperty({ 12 | type: 'string', 13 | example: '어린콩', 14 | description: '친구 닉네임', 15 | }) 16 | nickname: string; 17 | 18 | @ApiProperty({ 19 | type: 'string', 20 | example: 'https://imageurl.com', 21 | description: '친구 이미지 경로', 22 | }) 23 | image_url: string; 24 | } 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Social/Sources/Social/Model/ProfileHeaderItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 6/2/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct ProfileHeaderItem { 11 | let nickname: String 12 | let profileImageURL: String? 13 | let learningTime: Int 14 | 15 | public init(nickname: String, profileImageURL: String?, learningTime: Int) { 16 | self.nickname = nickname 17 | self.profileImageURL = profileImageURL 18 | self.learningTime = learningTime 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BE/src/auth/dto/response/update-info.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class UpdateInfoDto { 4 | @ApiProperty({ 5 | type: 'string', 6 | example: '어린콩', 7 | description: '닉네임', 8 | }) 9 | nickname: string; 10 | 11 | @ApiProperty({ 12 | type: 'string', 13 | example: 'example@co.kr', 14 | description: '이메일', 15 | }) 16 | email: string; 17 | 18 | @ApiProperty({ 19 | type: 'string', 20 | example: 'https://imageurl.com', 21 | description: '이미지 경로', 22 | }) 23 | image_url: string; 24 | } 25 | -------------------------------------------------------------------------------- /BE/src/users/users.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { UsersController } from './users.controller'; 3 | import { TypeOrmModule } from '@nestjs/typeorm'; 4 | import { UsersModel } from './entity/users.entity'; 5 | import { UsersService } from './users.service'; 6 | import { S3Service } from 'src/common/s3.service'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([UsersModel])], 10 | exports: [UsersService, TypeOrmModule], 11 | controllers: [UsersController], 12 | providers: [UsersService, S3Service], 13 | }) 14 | export class UsersModule {} 15 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/DefaultStudingPingUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultStudingPingUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public final class DefaultStudingPingUseCase: StudingPingUseCase { 11 | private let repository: StudyLogRepository 12 | 13 | public init(repository: StudyLogRepository) { 14 | self.repository = repository 15 | } 16 | 17 | public func studingPing() async throws { 18 | try await repository.studingPing() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/RepositoryProtocols/TimerRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerRepsoitory.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol TimerRepsoitory { 14 | func startTimer( 15 | startTime: Date, 16 | categoryId: Int? 17 | ) -> AnyPublisher 18 | 19 | func finishTimer( 20 | endTime: Date, 21 | learningTime: Int, 22 | categoryId: Int? 23 | ) -> AnyPublisher 24 | } 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/Entities/Category.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Category.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct StudyCategory: Hashable { 11 | public let id: Int 12 | public var color: String 13 | public var subject: String 14 | public var studyTime: Int? 15 | 16 | public init(id: Int, color: String, subject: String, studyTime: Int? = nil) { 17 | self.id = id 18 | self.color = color 19 | self.subject = subject 20 | self.studyTime = studyTime 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/RepositoryProtocols/FriendRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FriendRepository.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public protocol FriendRepository { 14 | func follow(at nickname: String) -> AnyPublisher 15 | func unfollow(at id: Int) -> AnyPublisher 16 | func search(at nickname: String) -> AnyPublisher 17 | func loadChart(at id: Int) -> AnyPublisher 18 | } 19 | -------------------------------------------------------------------------------- /BE/src/users/dto/create-user.dto.ts: -------------------------------------------------------------------------------- 1 | import { PickType } from '@nestjs/mapped-types'; 2 | import { UsersModel } from '../entity/users.entity'; 3 | import { ApiProperty } from '@nestjs/swagger'; 4 | 5 | export class CreateUserDto extends PickType(UsersModel, ['nickname', 'email']) { 6 | @ApiProperty({ 7 | type: 'string', 8 | example: '어린콩', 9 | description: '닉네임', 10 | required: false, 11 | }) 12 | nickname: string; 13 | 14 | @ApiProperty({ 15 | type: 'string', 16 | example: '2343242@ildskjf.com', 17 | description: '이메일', 18 | required: true, 19 | }) 20 | email: string; 21 | } 22 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/DefaultPatchTimeZoneUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultPatchTimeZoneUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public final class DefaultPatchTimeZoneUseCase: PatchTimeZoneUseCase { 11 | private let repository: UserInfoRepository 12 | 13 | public init(repository: UserInfoRepository) { 14 | self.repository = repository 15 | } 16 | 17 | public func patchTimeZone(date: Date) async throws { 18 | try await repository.patchTimeZone(date: date) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BE/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | pnpm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # OS 15 | .DS_Store 16 | 17 | # Invironment Variable 18 | .env 19 | 20 | # Tests 21 | /coverage 22 | /.nyc_output 23 | 24 | # IDEs and editors 25 | /.idea 26 | .project 27 | .classpath 28 | .c9/ 29 | *.launch 30 | .settings/ 31 | *.sublime-workspace 32 | 33 | # IDE - VSCode 34 | .vscode/* 35 | !.vscode/settings.json 36 | !.vscode/tasks.json 37 | !.vscode/launch.json 38 | !.vscode/extensions.json 39 | 40 | loadtest-mock.json -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/SocialScene/SocialDetailResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FriendChartResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 11/30/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct SocialDetailResponseDTO: Decodable { 11 | let myData: [Int] 12 | let followingData: [Int] 13 | let primaryCategory: String? 14 | 15 | private enum CodingKeys: String, CodingKey { 16 | case myData = "my_daily_data" 17 | case followingData = "following_daily_data" 18 | case primaryCategory = "following_primary_category" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Category/DefaultDeleteCategoryUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultDeleteCategoryUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public final class DefaultDeleteCategoryUseCase: DeleteCategoryUseCase { 11 | private let repository: CategoryRepository 12 | 13 | public init(repository: CategoryRepository) { 14 | self.repository = repository 15 | } 16 | 17 | public func deleteCategory(of id: Int) async throws { 18 | try await repository.deleteCategory(id: id) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Category/DefaultReadCategoryUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultReadCategoryUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public final class DefaultReadCategoryUseCase: ReadCategoryUseCase { 11 | private let repository: CategoryRepository 12 | 13 | public init(repository: CategoryRepository) { 14 | self.repository = repository 15 | } 16 | 17 | public func readCategory() async throws -> [StudyCategory] { 18 | return try await repository.readCategories() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/SocialScene/FriendsResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FriendsResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/30. 6 | // 7 | 8 | import Foundation 9 | 10 | struct FriendsResponseDTO: Decodable { 11 | var id: Int 12 | var nickname: String 13 | var imageURL: String? 14 | var totalTime: Int 15 | var startTime: String? 16 | 17 | private enum CodingKeys: String, CodingKey { 18 | case id, nickname 19 | case imageURL = "image_url" 20 | case totalTime = "total_time" 21 | case startTime = "started_at" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BE/src/categories/categories.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { CategoriesService } from './categories.service'; 3 | import { CategoriesController } from './categories.controller'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { Categories } from './categories.entity'; 6 | import { AuthModule } from 'src/auth/auth.module'; 7 | import { UsersModule } from 'src/users/users.module'; 8 | 9 | @Module({ 10 | imports: [TypeOrmModule.forFeature([Categories]), AuthModule, UsersModule], 11 | providers: [CategoriesService], 12 | controllers: [CategoriesController], 13 | }) 14 | export class CategoriesModule {} 15 | -------------------------------------------------------------------------------- /BE/src/heartbeat/heartbeat.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HeartbeatService } from './heartbeat.service'; 3 | import { HeartbeatController } from './heartbeat.controller'; 4 | import { AuthModule } from 'src/auth/auth.module'; 5 | import { UsersModule } from 'src/users/users.module'; 6 | import { RedisService } from 'src/common/redis.service'; 7 | import { StudyLogsModule } from 'src/study-logs/study-logs.module'; 8 | 9 | @Module({ 10 | imports: [AuthModule, UsersModule, StudyLogsModule], 11 | controllers: [HeartbeatController], 12 | providers: [HeartbeatService, RedisService], 13 | }) 14 | export class HeartbeatModule {} 15 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Chart/DefaultFetchWeeklyChartUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultFetchWeeklyChartUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public final class DefaultFetchWeeklyChartUseCase: FetchWeeklyChartUseCase { 11 | private let repository: ChartRepository 12 | 13 | public init(repository: ChartRepository) { 14 | self.repository = repository 15 | } 16 | 17 | public func fetchWeeklyChartLog(at date: Date) async throws -> WeeklyChartLog { 18 | return try await repository.fetchWeeklyLog() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/DefaultGetUserInfoUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultGetUserInfoUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public final class DefaultGetUserInfoUseCase: GetUserInfoUseCase { 14 | private let repository: UserInfoRepository 15 | 16 | public init(repository: UserInfoRepository) { 17 | self.repository = repository 18 | } 19 | 20 | public func getUserInfo() -> AnyPublisher { 21 | return repository.getUserInfo() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BE/src/study-logs/dto/response/daily-stat.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { CategoryDto } from 'src/categories/dto/response/category.dto'; 3 | 4 | export class dailyStatDto { 5 | @ApiProperty({ 6 | type: 'number', 7 | example: 10000, 8 | description: '해당 날짜의 총 학습 시간', 9 | }) 10 | total_time: number; 11 | 12 | @ApiProperty({ 13 | type: Array(CategoryDto), 14 | description: '학습 한 카테고리들', 15 | }) 16 | categories: object[]; 17 | 18 | @ApiProperty({ 19 | type: 'number', 20 | example: '3', 21 | description: '해당 날짜에 유저의 학습 시간이 전체 유저 중 상위 몇 %인지', 22 | }) 23 | percentage: number; 24 | } 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/ChartScene/WeeklyChartLogResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeeklyChartLogResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 12/6/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct WeeklyChartLogResponseDTO: Decodable { 11 | let totalTime: Int 12 | let dailyData: [Int] 13 | let primaryCategory: String? 14 | let percentage: Double 15 | 16 | private enum CodingKeys: String, CodingKey { 17 | case totalTime = "total_time" 18 | case dailyData = "daily_data" 19 | case primaryCategory = "primary_category" 20 | case percentage 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Chart/DefaultLoadChartUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultLoadChartUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public final class DefaultLoadChartUseCase: LoadChartUseCase { 14 | private let repository: FriendRepository 15 | 16 | public init(repository: FriendRepository) { 17 | self.repository = repository 18 | } 19 | 20 | public func loadChart(at id: Int) -> AnyPublisher { 21 | return repository.loadChart(at: id) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "DesignSystem", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | .library( 11 | name: "DesignSystem", 12 | targets: ["DesignSystem"]), 13 | ], 14 | 15 | targets: [ 16 | .target( 17 | name: "DesignSystem"), 18 | .testTarget( 19 | name: "DesignSystemTests", 20 | dependencies: ["DesignSystem"]), 21 | ] 22 | ) 23 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Data", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | .library(name: "Data", targets: ["Data"]), 11 | ], 12 | 13 | dependencies: [ 14 | .package(path: "../Domain") 15 | ], 16 | 17 | targets: [ 18 | .target(name: "Data", dependencies: [ .product(name: "Domain", package: "Domain")]), 19 | .testTarget(name: "DataTests", dependencies: ["Data"]), 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/TimerScene/TimerStartRequestDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerStartDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 11/21/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct TimerStartRequestDTO: Encodable { 11 | let date: String 12 | let createdAt: String 13 | let type: String 14 | let learningTime: Int 15 | let categoryID: Int? 16 | 17 | private enum CodingKeys: String, CodingKey { 18 | case date 19 | case createdAt = "created_at" 20 | case type 21 | case learningTime = "learning_time" 22 | case categoryID = "category_id" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/DefaultGetStudyLogUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultGetStudyLogUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public final class DefaultGetStudyLogUseCase: GetStudyLogUseCase { 14 | private let repository: StudyLogRepository 15 | 16 | public init(studyLogRepository: StudyLogRepository) { 17 | self.repository = studyLogRepository 18 | } 19 | 20 | public func getStudyLog() -> AnyPublisher { 21 | return repository.getUserInfo() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Social/DefaultGetFriendUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultGetFriendsUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public final class DefaultGetFriendsUseCase: GetFriendsUseCase { 14 | private let repsoitory: SocialRepository 15 | 16 | public init(repsoitory: SocialRepository) { 17 | self.repsoitory = repsoitory 18 | } 19 | 20 | public func getMyFriend(date: Date) -> AnyPublisher<[Friend], NetworkError> { 21 | return repsoitory.getMyFriend(date: date) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Friend/DefaultUnfollowFriendUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultUnfollowFriendUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public final class DefaultUnfollowFriendUseCase: UnfollowFriendUseCase { 14 | private let repository: FriendRepository 15 | 16 | public init(repository: FriendRepository) { 17 | self.repository = repository 18 | } 19 | 20 | public func unfollow(at id: Int) -> AnyPublisher { 21 | return repository.unfollow(at: id) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Service/Sources/CategoryService/CategoryServiceInterface/CategoryManageable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CategoryManageable.swift 3 | // 4 | // 5 | // Created by 임현규 on 6/14/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | import Domain 11 | 12 | public protocol CategoryManageable { 13 | var categoryDidChangePublisher: AnyPublisher<[StudyCategory], Never> { get } 14 | func replace(categories: [StudyCategory]) 15 | func change(category: StudyCategory) 16 | func removeCategory(categoryId: Int) 17 | func append(category: StudyCategory) 18 | func findCategory(categoryId: Int) -> StudyCategory? 19 | func numberOfCategory() -> Int 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/TimerScene/TimerFinishRequestDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerFinishRequestDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 11/21/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct TimerFinishRequestDTO: Encodable { 11 | let date: String 12 | let createdAt: String 13 | let type: String 14 | let learningTime: Int 15 | let categoryID: Int? 16 | 17 | private enum CodingKeys: String, CodingKey { 18 | case date 19 | case createdAt = "created_at" 20 | case type 21 | case learningTime = "learning_time" 22 | case categoryID = "category_id" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/TimerScene/TimerStartResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerStartResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 11/21/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct TimerStartResponseDTO: Decodable { 11 | let date: String 12 | let createdAt: String 13 | let type: String 14 | let learningTime: Int 15 | let categoryID: Int? 16 | 17 | private enum CodingKeys: String, CodingKey { 18 | case date 19 | case createdAt = "created_at" 20 | case type 21 | case learningTime = "learning_time" 22 | case categoryID = "category_id" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Category/DefaultCreateCategoryUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultCreateCategoryUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public final class DefaultCreateCategoryUseCase: CreateCategoryUseCase { 11 | private let repository: CategoryRepository 12 | 13 | public init(repository: CategoryRepository) { 14 | self.repository = repository 15 | } 16 | 17 | public func createCategory(name: String, colorCode: String) async throws -> Int { 18 | try await repository.createCategory(name: name, colorCode: colorCode) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/TimerScene/TimerFinishResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerFinishResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 11/21/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct TimerFinishResponseDTO: Decodable { 11 | let date: String 12 | let createdAt: String 13 | let type: String 14 | let learningTime: Int 15 | let categoryID: Int? 16 | 17 | private enum CodingKeys: String, CodingKey { 18 | case date 19 | case createdAt = "created_at" 20 | case type 21 | case learningTime = "learning_time" 22 | case categoryID = "category_id" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Friend/DefaultFollowFriendUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultFollowFriendUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public final class DefaultFollowFriendUseCase: FollowFriendUseCase { 14 | private let repository: FriendRepository 15 | 16 | public init(repository: FriendRepository) { 17 | self.repository = repository 18 | } 19 | 20 | public func follow(at nickname: String) -> AnyPublisher { 21 | return repository.follow(at: nickname) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Friend/DefaultSearchFriendUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultSearchFriendUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public final class DefaultSearchFriendUseCase: SearchFriendUseCase { 14 | private let repository: FriendRepository 15 | 16 | public init(repository: FriendRepository) { 17 | self.repository = repository 18 | } 19 | 20 | public func search(at nickname: String) -> AnyPublisher { 21 | return repository.search(at: nickname) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Social/DefaultFetchFriendUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultFetchFriendsUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public final class DefaultFetchFriendsUseCase: FetchFriendsUseCase { 14 | private let repsoitory: SocialRepository 15 | 16 | public init(repsoitory: SocialRepository) { 17 | self.repsoitory = repsoitory 18 | } 19 | 20 | public func fetchMyFriend(date: Date) -> AnyPublisher<[FriendStatus], NetworkError> { 21 | return repsoitory.fetchMyFriend(date: date) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BE/src/users/dto/update-user.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | import { UsersModel } from '../entity/users.entity'; 3 | import { ApiProperty } from '@nestjs/swagger'; 4 | import { IsOptional } from 'class-validator'; 5 | 6 | export class UpdateUserDto extends PartialType(UsersModel) { 7 | @ApiProperty({ 8 | type: 'string', 9 | example: '어린콩', 10 | description: '닉네임', 11 | required: false, 12 | }) 13 | @IsOptional() 14 | nickname?: string; 15 | 16 | @ApiProperty({ 17 | type: 'string', 18 | format: 'binary', 19 | description: '이미지 파일', 20 | required: false, 21 | }) 22 | @IsOptional() 23 | image?: Express.Multer.File; 24 | } 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Base/BaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/13. 6 | // 7 | 8 | import UIKit 9 | 10 | open class BaseViewController: UIViewController { 11 | open override func viewDidLoad() { 12 | super.viewDidLoad() 13 | self.view.backgroundColor = .systemBackground 14 | configureUI() 15 | bind() 16 | } 17 | 18 | /// 해당 메소드 내부에 UI Components 레이아웃을 작성해주세요 19 | open func configureUI() { 20 | 21 | } 22 | 23 | /// 해당 메소드 내부에 Combine bind 관련 로직을 작성해주세요. 24 | open func bind() { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/Entities/FriendSearchResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FriendSearchResult.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | // TODO: 얘는 Entity에 있으면 안될듯 11 | public enum FriendSearchStatus: Int { 12 | case alreayFriend = 20002 13 | case myself = 20001 14 | case notFriend = 20000 15 | case unknown = 0 16 | } 17 | 18 | public struct FriendSearchResult { 19 | public let status: FriendSearchStatus 20 | public let imageURL: String? 21 | 22 | public init(status: FriendSearchStatus, imageURL: String?) { 23 | self.status = status 24 | self.imageURL = imageURL 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Selection.ahap: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 1.0, 3 | "Metadata": 4 | { 5 | "Project" : "FlipMate", 6 | "Created" : "11 Dec 2023", 7 | "Description" : "무언가가 선택됨을 알리는 햅틱 진동" 8 | }, 9 | "Pattern": 10 | [ 11 | { 12 | "Event": 13 | { 14 | "Time": 0.0, 15 | "EventType": "HapticTransient", 16 | "EventParameters": 17 | [ 18 | { "ParameterID": "HapticIntensity", "ParameterValue": 0.5 }, 19 | { "ParameterID": "HapticSharpness", "ParameterValue": 0.5 } 20 | ] 21 | } 22 | } 23 | ] 24 | } 25 | 26 | -------------------------------------------------------------------------------- /BE/src/mates/dto/response/follower-info.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class FollowerInfoDto { 4 | @ApiProperty({ 5 | type: 'number', 6 | example: 1, 7 | description: '친구 관계 id', 8 | }) 9 | id: number; 10 | 11 | @ApiProperty({ 12 | type: 'string', 13 | example: '어린콩', 14 | description: '친구 닉네임', 15 | }) 16 | nickname: string; 17 | 18 | @ApiProperty({ 19 | type: 'string', 20 | example: 'https://imageurl.com', 21 | description: '친구 이미지 경로', 22 | }) 23 | image_url: string; 24 | 25 | @ApiProperty({ 26 | type: 'boolean', 27 | example: false, 28 | description: '이미 친구관게인지', 29 | }) 30 | is_followed: boolean; 31 | } 32 | -------------------------------------------------------------------------------- /BE/src/users/decorator/user.decorator.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ExecutionContext, 3 | InternalServerErrorException, 4 | createParamDecorator, 5 | } from '@nestjs/common'; 6 | import { UsersModel } from '../entity/users.entity'; 7 | 8 | export const User = createParamDecorator( 9 | (data: keyof UsersModel | undefined, context: ExecutionContext) => { 10 | const req = context.switchToHttp().getRequest(); 11 | 12 | const user = req.user as UsersModel; 13 | 14 | if (!user) { 15 | throw new InternalServerErrorException( 16 | 'User 데코레이터는 AccessTokenGuard와 함께 사용해야 합니다', 17 | ); 18 | } 19 | 20 | if (data) { 21 | return user[data]; 22 | } 23 | return user; 24 | }, 25 | ); 26 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Category/DefaultUpdateCategoryUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultUpdateCategoryUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public final class DefaultUpdateCategoryUseCase: UpdateCategoryUseCsae { 11 | private let repository: CategoryRepository 12 | 13 | public init(repository: CategoryRepository) { 14 | self.repository = repository 15 | } 16 | 17 | public func updateCategory(of id: Int, newName: String, newColorCode: String) async throws { 18 | try await repository.updateCategory(id: id, newName: newName, newColorCode: newColorCode) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Network/Tests/NetworkTests/Utils/MockKeychainManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/17/24. 6 | // 7 | 8 | import Foundation 9 | @testable import Core 10 | 11 | struct MockKeychainManager: KeychainManageable { 12 | func saveAccessToken(token: String) throws { 13 | } 14 | 15 | func getAccessToken() throws -> String { 16 | return "" 17 | } 18 | 19 | func deleteAccessToken() throws { 20 | } 21 | 22 | func saveAppleUserID(id: String) throws { 23 | } 24 | 25 | func getAppleUserID() throws -> String { 26 | return "" 27 | } 28 | 29 | func deleteAppleUserID() throws { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BE/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/DefaultValidateNicknameUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultValidateNicknameUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | 10 | import Core 11 | 12 | public final class DefaultValidateNicknameUseCase: ValidateNicknameUseCase { 13 | private let validator: NickNameValidatable 14 | 15 | public init(validator: NickNameValidatable) { 16 | self.validator = validator 17 | } 18 | 19 | public func isNickNameValid(_ nickName: String) -> NickNameValidationState { 20 | let validationState = validator.checkNickNameValidationState(nickName) 21 | return validationState 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Login/Sources/Login/Model/LoginType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 6/2/24. 6 | // 7 | 8 | import Foundation 9 | 10 | enum LoginType { 11 | case google 12 | case apple 13 | 14 | var buttonTitle: String { 15 | switch self { 16 | case .google: 17 | NSLocalizedString("googleLogin", comment: "") 18 | case .apple: 19 | NSLocalizedString("appleLogin", comment: "") 20 | } 21 | } 22 | 23 | // TODO: - 이미지 Assets 추가 후 작업 24 | var logoImage: String { 25 | switch self { 26 | case .google: 27 | "" 28 | case .apple: 29 | "" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BE/src/heartbeat/heartbeat.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, UseGuards } from '@nestjs/common'; 2 | import { HeartbeatService } from './heartbeat.service'; 3 | import { User } from 'src/users/decorator/user.decorator'; 4 | import { AccessTokenGuard } from 'src/auth/guard/bearer-token.guard'; 5 | import { ApiBearerAuth } from '@nestjs/swagger'; 6 | 7 | @Controller() 8 | export class HeartbeatController { 9 | constructor(private readonly heartbeatsService: HeartbeatService) {} 10 | 11 | @UseGuards(AccessTokenGuard) 12 | @Get('/heartbeat') 13 | @ApiBearerAuth() 14 | heartbeat(@User('id') userId: number) { 15 | this.heartbeatsService.recordHeartbeat(userId); 16 | return { statusCode: 200, message: 'OK' }; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BE/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import request from 'supertest'; 4 | import { AppModule } from '../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Timer/DefaultStartTimerUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultStartTimerUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | public final class DefaultStartTimerUseCase: StartTimerUseCase { 14 | private let timerRepository: TimerRepsoitory 15 | 16 | public init(timerRepository: TimerRepsoitory) { 17 | self.timerRepository = timerRepository 18 | } 19 | 20 | public func startTimer(startTime: Date, categoryId: Int?) -> AnyPublisher { 21 | return timerRepository.startTimer(startTime: startTime, categoryId: categoryId) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Social/DefaultSetupProfileInfoUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultSetupProfileInfoUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public final class DefaultSetupProfileInfoUseCase: SetupProfileInfoUseCase { 11 | private let repository: ProfileSettingsRepository 12 | 13 | public init(repository: ProfileSettingsRepository) { 14 | self.repository = repository 15 | } 16 | 17 | public func setupProfileInfo(nickName: String, profileImageData: Data) async throws -> UserInfo { 18 | return try await repository.setupNewProfileInfo(nickName: nickName, profileImageData: profileImageData) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/FlipMate.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "DCA1C60F-CB66-4CB2-9B87-4A08F1661784", 5 | "name" : "Test Scheme Action", 6 | "options" : { 7 | 8 | } 9 | } 10 | ], 11 | "defaultOptions" : { 12 | "targetForVariableExpansion" : { 13 | "containerPath" : "container:FlipMate.xcodeproj", 14 | "identifier" : "600908B92AFCD7DF0065DFFB", 15 | "name" : "FlipMate" 16 | } 17 | }, 18 | "testTargets" : [ 19 | { 20 | "parallelizable" : true, 21 | "target" : { 22 | "containerPath" : "container:FlipMate.xcodeproj", 23 | "identifier" : "600908CF2AFCD7E00065DFFB", 24 | "name" : "FlipMateTests" 25 | } 26 | } 27 | ], 28 | "version" : 1 29 | } 30 | -------------------------------------------------------------------------------- /BE/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "ES2021", 10 | "sourceMap": true, 11 | "outDir": "dist", 12 | "baseUrl": ".", 13 | "paths": { 14 | "src/*": ["src/*"] 15 | }, 16 | "incremental": true, 17 | "skipLibCheck": true, 18 | "strictNullChecks": false, 19 | "noImplicitAny": false, 20 | "strictBindCallApply": false, 21 | "forceConsistentCasingInFileNames": false, 22 | "noFallthroughCasesInSwitch": false, 23 | "resolveJsonModule": true, 24 | "esModuleInterop": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UICollectionViewCell++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionViewCell++Extension.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/20. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol ReusableView: AnyObject { 11 | static var defaultReuseIdentifier: String { get } 12 | } 13 | 14 | extension ReusableView where Self: UIView { 15 | static var defaultReuseIdentifier: String { 16 | return String(describing: self) 17 | } 18 | } 19 | 20 | extension UICollectionViewCell { 21 | public func configureCategoryCellLayer() { 22 | layer.borderWidth = 1.0 23 | layer.cornerRadius = 8.0 24 | layer.borderColor = FlipMateColor.gray2.color?.cgColor 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/StudyLogEndpoints.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudyLogEndpoints.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/23. 6 | // 7 | 8 | import Foundation 9 | import Network 10 | 11 | struct StudyLogEndpoints { 12 | static func getStudyLog() -> EndPoint { 13 | return EndPoint( 14 | baseURL: BaseURL.flipmateDomain, 15 | path: Paths.studylogs + "?date=\(Date().dateToString(format: .yyyyMMdd))", 16 | method: .get) 17 | } 18 | 19 | static func studingPing() -> EndPoint { 20 | return EndPoint( 21 | baseURL: BaseURL.flipmateDomain, 22 | path: Paths.ping, 23 | method: .get) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BE/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | tsconfigRootDir: __dirname, 6 | sourceType: 'module', 7 | }, 8 | plugins: ['@typescript-eslint/eslint-plugin'], 9 | extends: [ 10 | 'plugin:@typescript-eslint/recommended', 11 | 'plugin:prettier/recommended', 12 | ], 13 | root: true, 14 | env: { 15 | node: true, 16 | jest: true, 17 | }, 18 | ignorePatterns: ['.eslintrc.js'], 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/explicit-module-boundary-types': 'off', 23 | '@typescript-eslint/no-explicit-any': 'off', 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Authentication/DefaultWithdrawUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultWithdrawUesCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | import Core 11 | 12 | public final class DefaultWithdrawUesCase: WithdrawUseCase { 13 | private let repository: AuthenticationRepository 14 | 15 | public init(repository: AuthenticationRepository) { 16 | self.repository = repository 17 | } 18 | 19 | public func withdraw() async throws { 20 | try await repository.withdraw() 21 | signOut() 22 | } 23 | 24 | private func signOut() { 25 | NotificationCenter.default.post(name: NotificationName.signOut, object: nil) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BE/src/common/const/env-keys.const.ts: -------------------------------------------------------------------------------- 1 | export enum ENV { 2 | IMAGE_ENDPOINT = 'IMAGE_ENDPOINT', 3 | IMAGE_ACCESSKEY = 'IMAGE_ACCESSKEY', 4 | IMAGE_SECRETKEY = 'IMAGE_SECRETKEY', 5 | IMAGE_REGION = 'IMAGE_REGION', 6 | IMAGE_BUCKET = 'IMAGE_BUCKET', 7 | CDN_ENDPOINT = 'CDN_ENDPOINT', 8 | JWT_SECRET = 'JWT_SECRET', 9 | JWT_EXPIRES_IN = 'JWT_EXPIRES_IN', 10 | DATABASE_HOST = 'DATABASE_HOST', 11 | DATABASE_PORT = 'DATABASE_PORT', 12 | DATABASE_USERNAME = 'DATABASE_USERNAME', 13 | DATABASE_PASSWORD = 'DATABASE_PASSWORD', 14 | DATABASE_NAME = 'DATABASE_NAME', 15 | SSL = 'SSL', 16 | HTTPS_KEY_PATH = 'HTTPS_KEY_PATH', 17 | HTTPS_CERT_PATH = 'HTTPS_CERT_PATH', 18 | PORT = 'PORT', 19 | GREENEYE_URL = 'GREENEYE_URL', 20 | GREENEYE_SECRET = 'GREENEYE_SECRET', 21 | } 22 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Sources/Core/Extension/Date++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date++Extension.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/14/24. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Date { 11 | public enum FMDateFormmat: String { 12 | case yyyyMMdd = "yyyy-MM-dd" 13 | case yyyyMMddTHHmmSS = "yyyy-MM-dd'T'HH:mm:ss" 14 | case yyyyMMddhhmmssZZZZZ = "yyyy-MM-dd HH:mm:ssZZZZZ" 15 | case ZZZZZ = "ZZZZZ" 16 | case day = "dd" 17 | } 18 | 19 | /// 파라미터로 전달받은 format의 타입으로 Date을 문자열로 변환하여 리턴합니다. 20 | public func dateToString(format: FMDateFormmat) -> String { 21 | let formatter = DateFormatter() 22 | formatter.dateFormat = format.rawValue 23 | return formatter.string(from: self) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BE/src/study-logs/study-logs.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { StudyLogsService } from './study-logs.service'; 3 | import { StatsController, StudyLogsController } from './study-logs.controller'; 4 | import { StudyLogs } from './study-logs.entity'; 5 | import { TypeOrmModule } from '@nestjs/typeorm'; 6 | import { AuthModule } from 'src/auth/auth.module'; 7 | import { UsersModule } from 'src/users/users.module'; 8 | import { RedisService } from 'src/common/redis.service'; 9 | 10 | @Module({ 11 | imports: [TypeOrmModule.forFeature([StudyLogs]), AuthModule, UsersModule], 12 | providers: [StudyLogsService, RedisService], 13 | controllers: [StudyLogsController, StatsController], 14 | exports: [StudyLogsService, TypeOrmModule], 15 | }) 16 | export class StudyLogsModule {} 17 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Service/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Service", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | .library(name: "CategoryService", targets: ["CategoryService"]), 11 | ], 12 | 13 | dependencies: [ 14 | .package(name: "Domain", path: "../Domain") 15 | ], 16 | 17 | targets: [ 18 | .target(name: "CategoryService", dependencies: [ 19 | .product(name: "Domain", package: "Domain") 20 | ]), 21 | .testTarget(name: "ServiceTests", dependencies: [ 22 | "CategoryService" 23 | ]), 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/BaseComponents.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseURL.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum BaseURL { 11 | static let flipmateDomain = "https://flipmate.site:3000" 12 | static let developDomain = "https://flipmate.site:3000" 13 | } 14 | 15 | enum Paths { 16 | static let categories = "/categories" 17 | static let googleApp = "/auth/google/app" 18 | static let appleApp = "/auth/apple/app" 19 | static let studylogs = "/study-logs" 20 | static let auth = "/auth" 21 | static let authInfo = "/auth/info" 22 | static let nickNameValidation = "/user/nickname-validation" 23 | static let friend = "/mates" 24 | static let user = "/user" 25 | static let ping = "/heartbeat" 26 | } 27 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/Entities/Friend.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Friend.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/18/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct Friend: Hashable { 11 | public let id: Int 12 | public let nickName: String 13 | public let profileImageURL: String? 14 | public let totalTime: Int 15 | public let startedTime: String? 16 | public let isStuding: Bool 17 | 18 | public init(id: Int, nickName: String, profileImageURL: String?, totalTime: Int, startedTime: String?, isStuding: Bool) { 19 | self.id = id 20 | self.nickName = nickName 21 | self.profileImageURL = profileImageURL 22 | self.totalTime = totalTime 23 | self.startedTime = startedTime 24 | self.isStuding = isStuding 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /BE/src/mates/mates.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MatesController } from './mates.controller'; 3 | import { MatesService } from './mates.service'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { Mates } from './mates.entity'; 6 | import { AuthModule } from 'src/auth/auth.module'; 7 | import { UsersModule } from 'src/users/users.module'; 8 | import { RedisService } from 'src/common/redis.service'; 9 | import { StudyLogsModule } from 'src/study-logs/study-logs.module'; 10 | 11 | @Module({ 12 | imports: [ 13 | TypeOrmModule.forFeature([Mates]), 14 | AuthModule, 15 | UsersModule, 16 | StudyLogsModule, 17 | ], 18 | controllers: [MatesController], 19 | providers: [MatesService, RedisService], 20 | exports: [MatesService], 21 | }) 22 | export class MatesModule {} 23 | -------------------------------------------------------------------------------- /BE/src/study-logs/dto/response/weekly-stats.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class weeklyStatDto { 4 | @ApiProperty({ 5 | type: 'number', 6 | example: 10000, 7 | description: '일주일동안의 총 학습 시간', 8 | }) 9 | total_time: number; 10 | 11 | @ApiProperty({ 12 | type: 'array', 13 | items: { type: 'number' }, 14 | example: [0, 0, 13, 12, 33, 12, 4], 15 | description: '일주일동안 학습한 시간들 배열 (길이 7)', 16 | }) 17 | daily_data: number[]; 18 | 19 | @ApiProperty({ 20 | type: 'string', 21 | example: '백준', 22 | description: '일주일동안 가장 주된 카테고리', 23 | }) 24 | primary_category: string; 25 | 26 | @ApiProperty({ 27 | type: 'number', 28 | example: '3.33', 29 | description: '해당 날짜에 유저의 학습 시간이 전체 유저 중 상위 몇 %인지', 30 | }) 31 | percentage: number; 32 | } 33 | -------------------------------------------------------------------------------- /BE/src/auth/dto/response/get-info.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class GetInfoDto { 4 | @ApiProperty({ 5 | type: 'string', 6 | example: '어린콩', 7 | description: '닉네임', 8 | }) 9 | nickname: string; 10 | 11 | @ApiProperty({ 12 | type: 'string', 13 | example: 'example@co.kr', 14 | description: '이메일', 15 | }) 16 | email: string; 17 | 18 | @ApiProperty({ 19 | type: 'string', 20 | example: 'https://imageurl.com', 21 | description: '이미지 경로', 22 | }) 23 | image_url: string; 24 | 25 | @ApiProperty({ 26 | type: 'number', 27 | example: 20, 28 | description: '팔로워 수', 29 | }) 30 | follower_count: number; 31 | 32 | @ApiProperty({ 33 | type: 'number', 34 | example: 10, 35 | description: '팔로잉 수', 36 | }) 37 | following_count: number; 38 | } 39 | -------------------------------------------------------------------------------- /BE/src/common/redis.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { RedisClientType, createClient } from 'redis'; 3 | 4 | @Injectable() 5 | export class RedisService { 6 | private client: RedisClientType; 7 | constructor() { 8 | this.client = createClient(); 9 | this.client.connect(); 10 | } 11 | async hset(key: string, field: string, value: string) { 12 | await this.client.hSet(key, field, value); 13 | } 14 | 15 | hget(key: string, field: string): Promise { 16 | return this.client.hGet(key, field); 17 | } 18 | 19 | async hdel(key: string, field: string): Promise { 20 | await this.client.hDel(key, field); 21 | } 22 | 23 | async del(key: string) { 24 | await this.client.del(key); 25 | } 26 | 27 | getKeys() { 28 | return this.client.keys('*'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Social/Sources/Social/Model/UpdateFriend.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 6/2/24. 6 | // 7 | 8 | import Foundation 9 | 10 | // class일 필요가 있을까? 11 | public final class UpdateFriend { 12 | var id: Int 13 | var currentLearningTime: Int 14 | 15 | public init(id: Int, currentLearningTime: Int) { 16 | self.id = id 17 | self.currentLearningTime = currentLearningTime 18 | } 19 | } 20 | 21 | extension UpdateFriend: Hashable { 22 | public static func == (lhs: UpdateFriend, rhs: UpdateFriend) -> Bool { 23 | return lhs.id == rhs.id && lhs.currentLearningTime == rhs.currentLearningTime 24 | } 25 | 26 | public func hash(into hasher: inout Hasher) { 27 | hasher.combine(id) 28 | hasher.combine(currentLearningTime) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Network/Sources/Network/URLSessionable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSessionable.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/15/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | public protocol URLSessionable { 12 | typealias APIResponse = (data: Data, response: URLResponse) 13 | func response(for request: URLRequest) -> AnyPublisher 14 | func response(for request: URLRequest) async throws -> APIResponse 15 | } 16 | 17 | extension URLSession: URLSessionable { 18 | public func response(for request: URLRequest) -> AnyPublisher { 19 | return dataTaskPublisher(for: request).eraseToAnyPublisher() 20 | } 21 | 22 | public func response(for request: URLRequest) async throws -> APIResponse { 23 | return try await data(for: request) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/ChartEndpoints.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartEndpoints.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 12/5/23. 6 | // 7 | 8 | import Foundation 9 | import Network 10 | 11 | struct ChartEndpoints { 12 | static func fetchDailyLog(date: Date) -> EndPoint { 13 | return EndPoint( 14 | baseURL: BaseURL.flipmateDomain, 15 | path: Paths.studylogs + "/stats" + "?date=\(date.dateToString(format: .yyyyMMdd))", 16 | method: .get) 17 | } 18 | 19 | static func fetchWeeklyLog() -> EndPoint { 20 | let date = Date() 21 | return EndPoint( 22 | baseURL: BaseURL.flipmateDomain, 23 | path: Paths.studylogs + "/stats/weekly" + "?date=\(date.dateToString(format: .yyyyMMdd))", method: .get) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Timer/DefaultFinishTimerUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultFinishTimerUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Core 12 | 13 | // TODO: UseCase의 구현체들이 class일 필요가 있을까? 14 | public final class DefaultFinishTimerUseCase: FinishTimerUseCase { 15 | private let timerRepository: TimerRepsoitory 16 | 17 | public init(timerRepository: TimerRepsoitory) { 18 | self.timerRepository = timerRepository 19 | } 20 | 21 | public func finishTimer(endTime: Date, learningTime: Int, categoryId: Int?) -> AnyPublisher { 22 | return timerRepository.finishTimer( 23 | endTime: endTime, 24 | learningTime: learningTime, 25 | categoryId: categoryId) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BE/src/mates/mates.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | ManyToOne, 5 | JoinColumn, 6 | Column, 7 | } from 'typeorm'; 8 | import { UsersModel } from 'src/users/entity/users.entity'; 9 | 10 | @Entity() 11 | export class Mates { 12 | @PrimaryGeneratedColumn() 13 | id: number; 14 | 15 | @ManyToOne(() => UsersModel, (user) => user.follower, { 16 | eager: true, 17 | onDelete: 'CASCADE', 18 | }) 19 | @JoinColumn({ name: 'follower_id' }) 20 | follower_id: UsersModel; 21 | 22 | @ManyToOne(() => UsersModel, (user) => user.following, { 23 | eager: true, 24 | onDelete: 'CASCADE', 25 | }) 26 | @JoinColumn({ name: 'following_id' }) 27 | following_id: UsersModel; 28 | 29 | @Column({ type: 'boolean', default: false }) 30 | is_fixed: boolean; 31 | 32 | @Column({ type: 'boolean', default: false }) 33 | is_blocked: boolean; 34 | } 35 | -------------------------------------------------------------------------------- /BE/src/study-logs/dto/request/create-study-logs.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class StudyLogsCreateDto { 4 | @ApiProperty({ 5 | example: '2023-11-23', 6 | description: '학습을 시작한 날짜', 7 | }) 8 | date: string; 9 | 10 | @ApiProperty({ 11 | type: 'date', 12 | example: '2023-11-23 11:00:12+09:00', 13 | description: '학습을 시작/종료 시점의 시간', 14 | }) 15 | created_at: string; 16 | 17 | @ApiProperty({ 18 | type: 'enum', 19 | example: 'start', 20 | description: '학습이 시작인지 종료인지에 대한 타입', 21 | }) 22 | type: 'start' | 'finish'; 23 | 24 | @ApiProperty({ 25 | type: 'number', 26 | example: 3600, 27 | description: '학습시간 (초 단위)', 28 | }) 29 | learning_time: number; 30 | 31 | @ApiProperty({ 32 | type: 'number', 33 | example: 1, 34 | description: '카테고리 아이디', 35 | }) 36 | category_id: number; 37 | } 38 | -------------------------------------------------------------------------------- /BE/test/mock-table/study-logs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "date": "2023-11-29", 5 | "created_at": "2023-11-29 12:11:12", 6 | "type": "finish", 7 | "learning_time": 315, 8 | "user_id": 1, 9 | "category_id": null 10 | }, 11 | { 12 | "id": 2, 13 | "date": "2023-11-29", 14 | "created_at": "2023-11-29 15:11:12", 15 | "type": "finish", 16 | "learning_time": 512, 17 | "user_id": 1, 18 | "category_id": 1 19 | }, 20 | { 21 | "id": 3, 22 | "date": "2023-11-29", 23 | "created_at": "2023-11-29 12:11:12", 24 | "type": "finish", 25 | "learning_time": 315, 26 | "user_id": 2, 27 | "category_id": null 28 | }, 29 | { 30 | "id": 4, 31 | "date": "2023-11-29", 32 | "created_at": "2023-11-29 15:11:12", 33 | "type": "finish", 34 | "learning_time": 510, 35 | "user_id": 2, 36 | "category_id": 3 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/UserInfoEndpoints.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfoEndpoints.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/12/13. 6 | // 7 | 8 | import Foundation 9 | import Network 10 | 11 | struct UserInfoEndpoints { 12 | static func userInfo() -> EndPoint { 13 | return EndPoint( 14 | baseURL: BaseURL.flipmateDomain, 15 | path: Paths.authInfo, 16 | method: .get) 17 | } 18 | 19 | static func patchTimeZone(with timeZoneRequestDTO: TimeZoneRequestDTO) -> EndPoint { 20 | let encoder = JSONEncoder() 21 | let data = try? encoder.encode(timeZoneRequestDTO) 22 | return EndPoint( 23 | baseURL: BaseURL.flipmateDomain, 24 | path: Paths.auth + "/timezone", 25 | method: .patch, 26 | data: data) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Authentication/DefaultGoogleLoginUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultGoogleLoginUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | import Core 11 | 12 | public final class DefaultGoogleLoginUseCase: GoogleLoginUseCase { 13 | private let repository: AuthenticationRepository 14 | private let keychainManager = KeychainManager() 15 | 16 | public init(repository: AuthenticationRepository) { 17 | self.repository = repository 18 | } 19 | 20 | public func googleLogin(accessToken: String) async throws -> User { 21 | let response = try await repository.googleLogin(with: accessToken) 22 | let userAccessToken = response.accessToken 23 | try keychainManager.saveAccessToken(token: userAccessToken) 24 | return response 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Core", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, making them visible to other packages. 11 | .library( 12 | name: "Core", 13 | targets: ["Core"]), 14 | ], 15 | targets: [ 16 | // Targets are the basic building blocks of a package, defining a module or a test suite. 17 | // Targets can depend on other targets in this package and products from dependencies. 18 | .target( 19 | name: "Core"), 20 | .testTarget( 21 | name: "CoreTests", 22 | dependencies: ["Core"]), 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /BE/src/auth/google.strategy.ts: -------------------------------------------------------------------------------- 1 | import { PassportStrategy } from '@nestjs/passport'; 2 | import { Strategy, VerifyCallback } from 'passport-google-oauth20'; 3 | import { Injectable } from '@nestjs/common'; 4 | import { ConfigService } from '@nestjs/config'; 5 | 6 | @Injectable() 7 | export class GoogleStrategy extends PassportStrategy(Strategy, 'google') { 8 | constructor(private config: ConfigService) { 9 | super({ 10 | clientID: config.get('GOOGLE_CLIENT_ID'), 11 | clientSecret: config.get('GOOGLE_CLIENT_SECRET'), 12 | callbackURL: config.get('GOOGLE_CALLBACK_URL'), 13 | scope: ['email'], 14 | }); 15 | } 16 | 17 | async validate( 18 | accessToken: string, 19 | refreshToken: string, 20 | email: any, 21 | done: VerifyCallback, 22 | ): Promise { 23 | const user = { 24 | email: email.emails[0].value, 25 | auth_type: 'google', 26 | }; 27 | done(null, user); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UIImageView++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView++Extension.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/12/05. 6 | // 7 | 8 | import UIKit 9 | import FMImageProvider 10 | 11 | extension UIImageView { 12 | private func downLoadImage(with url: URL) async throws { 13 | let image = try await FMImageProvider.shared.fetchImageData(from: url) 14 | self.image = UIImage(data: image) 15 | } 16 | 17 | public func setImage(url: String?) { 18 | Task { 19 | do { 20 | guard let imageURL = URL(string: url ?? "") else { 21 | self.image = .profileImage 22 | return 23 | } 24 | try await self.downLoadImage(with: imageURL) 25 | } catch { 26 | self.image = .profileImage 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/be-main-workflow.yaml: -------------------------------------------------------------------------------- 1 | name: BE main Workflow 2 | 3 | on: 4 | push: 5 | branches: main 6 | paths: "BE/**" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | 16 | - name: connect to NCP 17 | uses: appleboy/ssh-action@master 18 | with: 19 | host: ${{ secrets.NCP_HOST }} 20 | username: ${{ secrets.NCP_USERNAME }} 21 | password: ${{ secrets.NCP_PASSWORD }} 22 | 23 | script: | 24 | export NVM_DIR=~/.nvm 25 | source ~/.nvm/nvm.sh 26 | cd iOS06-FlipMate/BE 27 | git pull origin main 28 | npm install 29 | if ! pm2 list | grep "flipmate-dev"; then 30 | pm2 start npm --name "flipmate-dev" -- run start 31 | else 32 | pm2 restart flipmate-dev 33 | fi 34 | -------------------------------------------------------------------------------- /BE/src/common/config/multer.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { BadRequestException } from '@nestjs/common'; 3 | import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface'; 4 | import multer from 'multer'; 5 | 6 | export const multerConfig = (): MulterOptions => { 7 | const storage = multer.memoryStorage(); 8 | const fileFilter = (req, file, callback) => { 9 | const ext = path.extname(file.originalname); 10 | if (ext !== '.jpg' && ext !== '.jpeg' && ext !== '.png') { 11 | return callback( 12 | new BadRequestException({ 13 | statusCode: 40002, 14 | message: 'jpg/jpeg/png 파일만 업로드 가능합니다!', 15 | error: 'Bad Request', 16 | }), 17 | ); 18 | } 19 | return callback(null, true); 20 | }; 21 | const limits = { fileSize: 1024 * 1024 * 10 }; //10MB 22 | 23 | return { 24 | storage, 25 | fileFilter, 26 | limits, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /BE/src/auth/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AuthService } from './auth.service'; 3 | import { JwtService } from '@nestjs/jwt'; 4 | import { UsersService } from 'src/users/users.service'; 5 | import { MockUsersService } from '../../test/mock-service/mock-user-service'; 6 | 7 | describe('AuthService', () => { 8 | let service: AuthService; 9 | 10 | beforeEach(async () => { 11 | const module: TestingModule = await Test.createTestingModule({ 12 | providers: [ 13 | AuthService, 14 | { 15 | provide: JwtService, 16 | useValue: {}, 17 | }, 18 | { 19 | provide: UsersService, 20 | useClass: MockUsersService, 21 | }, 22 | ], 23 | }).compile(); 24 | 25 | service = module.get(AuthService); 26 | }); 27 | 28 | it('should be defined', () => { 29 | expect(service).toBeDefined(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /BE/src/common/config/typeorm.config.ts: -------------------------------------------------------------------------------- 1 | import { ConfigService } from '@nestjs/config'; 2 | import { TypeOrmModuleOptions } from '@nestjs/typeorm'; 3 | import { Categories } from 'src/categories/categories.entity'; 4 | import { Mates } from 'src/mates/mates.entity'; 5 | import { StudyLogs } from 'src/study-logs/study-logs.entity'; 6 | import { UsersModel } from 'src/users/entity/users.entity'; 7 | import { ENV } from '../const/env-keys.const'; 8 | 9 | export const typeormConfig = (config: ConfigService): TypeOrmModuleOptions => ({ 10 | type: 'mysql', 11 | host: config.get(ENV.DATABASE_HOST), 12 | port: config.get(ENV.DATABASE_PORT), 13 | username: config.get(ENV.DATABASE_USERNAME), 14 | password: config.get(ENV.DATABASE_PASSWORD), 15 | database: config.get(ENV.DATABASE_NAME), 16 | charset: 'utf8mb4_unicode_ci', 17 | entities: [StudyLogs, Categories, UsersModel, Mates], 18 | timezone: 'Z', 19 | synchronize: true, 20 | }); 21 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/DefaultProfileImage.imageset/DefaultProfileImage.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Authentication/DefaultAppleLoginUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultAppleLoginUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/20/24. 6 | // 7 | 8 | import Foundation 9 | 10 | import Core 11 | 12 | public final class DefaultAppleLoginUseCase: AppleLoginUseCase { 13 | private let repository: AuthenticationRepository 14 | private let keychainManager = KeychainManager() 15 | 16 | public init(repository: AuthenticationRepository) { 17 | self.repository = repository 18 | } 19 | 20 | public func appleLogin(accessToken: String, userID: String) async throws -> User { 21 | let response = try await repository.appleLogin(with: accessToken) 22 | let userAccessToken = response.accessToken 23 | try keychainManager.saveAccessToken(token: userAccessToken) 24 | try keychainManager.saveAppleUserID(id: userID) 25 | return response 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/Gray1.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.350", 9 | "green" : "0.350", 10 | "red" : "0.350" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.891", 27 | "green" : "0.891", 28 | "red" : "0.891" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /BE/test/mock-service/mock-study-logs-service.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import studyLogsData from '../mock-table/study-logs.json'; 3 | 4 | export class MockStudyLogsService { 5 | private data = studyLogsData; 6 | 7 | calculateTotalTimes(id, start_date, end_date) { 8 | const startMoment = moment(start_date); 9 | const diffDays = moment(end_date).diff(startMoment, 'days') + 1; 10 | const result = Array.from({ length: diffDays }, () => 0); 11 | const daily_sums = this.data 12 | .filter( 13 | (studyLog) => 14 | studyLog.user_id === id && 15 | studyLog.date >= start_date && 16 | studyLog.date <= end_date, 17 | ) 18 | .reduce((acc, cur) => { 19 | const index = moment(cur.date).diff(startMoment, 'days'); 20 | acc[index] += cur.learning_time; 21 | return acc; 22 | }, result); 23 | return daily_sums; 24 | } 25 | 26 | getPrimaryCategory(id, start_date, end_date) {} 27 | } 28 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/SocialEndpoints.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SocialEndpoints.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/30. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | import Network 11 | 12 | struct SocialEndpoints { 13 | static func getMyFreinds(date: Date) -> EndPoint<[FriendsResponseDTO]> { 14 | return EndPoint( 15 | baseURL: BaseURL.flipmateDomain, 16 | path: Paths.friend + "?datetime=\(date.dateToString(format: .yyyyMMddTHHmmSS))&timezone=\(date.dateToString(format: .ZZZZZ))", 17 | method: .get) 18 | 19 | } 20 | 21 | static func fetchMyFriend(date: Date) -> EndPoint<[FriendsResponseDTO]> { 22 | return EndPoint( 23 | baseURL: BaseURL.flipmateDomain, 24 | path: Paths.friend + "?datetime=\(date.dateToString(format: .yyyyMMddTHHmmSS))&timezone=\(date.dateToString(format: .ZZZZZ))", 25 | method: .get) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/Gray3.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.891", 9 | "green" : "0.891", 10 | "red" : "0.891" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "display-p3", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.346", 27 | "green" : "0.350", 28 | "red" : "0.345" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/TabBarColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "1.000", 9 | "green" : "1.000", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.362", 27 | "green" : "0.205", 28 | "red" : "0.187" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOS/FMImageProvider/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.9 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "FMImageProvider", 8 | platforms: [.iOS(.v14), .macOS(.v12)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, making them visible to other packages. 11 | .library( 12 | name: "FMImageProvider", 13 | targets: ["FMImageProvider"]), 14 | ], 15 | targets: [ 16 | // Targets are the basic building blocks of a package, defining a module or a test suite. 17 | // Targets can depend on other targets in this package and products from dependencies. 18 | .target( 19 | name: "FMImageProvider"), 20 | .testTarget( 21 | name: "FMImageProviderTests", 22 | dependencies: ["FMImageProvider"]), 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/ApproveGreen.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x06", 9 | "green" : "0xFF", 10 | "red" : "0x5E" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "display-p3", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x06", 27 | "green" : "0xFF", 28 | "red" : "0x5E" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/TabBarLayerColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.553", 9 | "green" : "0.553", 10 | "red" : "0.553" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.362", 27 | "green" : "0.205", 28 | "red" : "0.187" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/WarningRed.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x11", 9 | "green" : "0x3F", 10 | "red" : "0xFF" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "display-p3", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x11", 27 | "green" : "0x3F", 28 | "red" : "0xFF" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FilpMateColor/TabBarIconSelected.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.000", 9 | "green" : "0.000", 10 | "red" : "0.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "display-p3", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.852", 27 | "green" : "0.844", 28 | "red" : "0.862" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Sources/Core/Error/NetworkError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkError.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/14/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum NetworkError: LocalizedError, Equatable { 11 | case invalidURLComponents 12 | case invalidResponse 13 | case invalidToken 14 | case statusCodeError(statusCode: Int, message: String) 15 | case bodyEmpty 16 | case typeCastingFailed 17 | case unknown 18 | 19 | public var errorDescription: String? { 20 | switch self { 21 | case .invalidURLComponents: return "URLComponents 초기화 실패." 22 | case .invalidResponse: return "Response 타입 HTTPURLResponse로 변환 실패" 23 | case .invalidToken: return "토큰 만료" 24 | case .statusCodeError(_, let message): return message 25 | case .bodyEmpty: return "Response body가 비어있음" 26 | case .typeCastingFailed: return "형변환 실패" 27 | case .unknown: return "알 수 없는 에러" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/ChartScene/DailyChartLogResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartLogResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 12/5/23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct DailyChartLogResponseDTO: Decodable { 11 | let todayTime: Int 12 | let categories: [CategoryDTO]? 13 | let percentage: Double 14 | 15 | private enum CodingKeys: String, CodingKey { 16 | case todayTime = "total_time" 17 | case categories 18 | case percentage 19 | } 20 | } 21 | 22 | extension DailyChartLogResponseDTO { 23 | struct CategoryDTO: Decodable { 24 | let id: Int 25 | let name: String 26 | let color: String 27 | let todayTime: Int 28 | 29 | private enum CodingKeys: String, CodingKey { 30 | case id 31 | case name 32 | case color = "color_code" 33 | case todayTime = "today_time" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/be-pre-production-workflow.yaml: -------------------------------------------------------------------------------- 1 | name: BE Pre Production Workflow 2 | 3 | on: 4 | push: 5 | branches: preproduction 6 | paths: "BE/**" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | 16 | - name: connect to NCP 17 | uses: appleboy/ssh-action@master 18 | with: 19 | host: ${{ secrets.NCP_HOST }} 20 | username: ${{ secrets.NCP_USERNAME }} 21 | password: ${{ secrets.NCP_PASSWORD }} 22 | 23 | script: | 24 | export NVM_DIR=~/.nvm 25 | source ~/.nvm/nvm.sh 26 | cd iOS06-FlipMate-preproduction/BE 27 | git pull origin preproduction 28 | npm install 29 | if ! pm2 list | grep "flipmate-preproduction"; then 30 | pm2 start npm --name "flipmate-preproduction" -- run start 31 | else 32 | pm2 restart flipmate-preproduction 33 | fi 34 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Core/Sources/Core/Logger/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FMLogger.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/14/24. 6 | // 7 | 8 | import OSLog 9 | 10 | enum FMConstant { 11 | static let bundleID = Bundle.main.bundleIdentifier ?? "" 12 | } 13 | 14 | public enum FMLogger { 15 | public static let general = Logger(subsystem: FMConstant.bundleID, category: "general") 16 | public static let user = Logger(subsystem: FMConstant.bundleID, category: "user") 17 | public static let device = Logger(subsystem: FMConstant.bundleID, category: "device") 18 | public static let viewLifeCycle = Logger(subsystem: FMConstant.bundleID, category: "viewLifeCycle") 19 | public static let appLifeCycle = Logger(subsystem: FMConstant.bundleID, category: "appLifeCycle") 20 | public static let timer = Logger(subsystem: FMConstant.bundleID, category: "timer") 21 | public static let friend = Logger(subsystem: FMConstant.bundleID, category: "friend") 22 | public static let chart = Logger(subsystem: FMConstant.bundleID, category: "chart") 23 | } 24 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Repositories/DefaultStudyLogRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultStudyLogRespository.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/23. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Domain 12 | import Network 13 | import Core 14 | 15 | public final class DefaultStudyLogRepository: StudyLogRepository { 16 | private let provider: Providable 17 | 18 | public init(provider: Providable) { 19 | self.provider = provider 20 | } 21 | 22 | public func getUserInfo() -> AnyPublisher { 23 | let endpoint = StudyLogEndpoints.getStudyLog() 24 | 25 | return provider.request(with: endpoint) 26 | .map { response -> StudyLog in 27 | return response.toEntity() 28 | } 29 | .eraseToAnyPublisher() 30 | } 31 | 32 | public func studingPing() async throws { 33 | let endpoint = StudyLogEndpoints.studingPing() 34 | 35 | _ = try await provider.request(with: endpoint) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /BE/src/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule, ConfigService } from '@nestjs/config'; 3 | import { AuthService } from './auth.service'; 4 | import { AuthController } from './auth.controller'; 5 | import { UsersModule } from 'src/users/users.module'; 6 | import { JwtModule } from '@nestjs/jwt'; 7 | import { GoogleStrategy } from './google.strategy'; 8 | import { MulterModule } from '@nestjs/platform-express'; 9 | import { multerConfig } from 'src/common/config/multer.config'; 10 | import { jwtConfig } from 'src/common/config/jwt.config'; 11 | 12 | @Module({ 13 | imports: [ 14 | JwtModule.registerAsync({ 15 | imports: [ConfigModule], 16 | inject: [ConfigService], 17 | useFactory: jwtConfig, 18 | }), 19 | MulterModule.registerAsync({ 20 | imports: [ConfigModule], 21 | inject: [ConfigService], 22 | useFactory: multerConfig, 23 | }), 24 | UsersModule, 25 | ], 26 | controllers: [AuthController], 27 | providers: [AuthService, GoogleStrategy], 28 | exports: [AuthService], 29 | }) 30 | export class AuthModule {} 31 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Extensions/Data++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data++Extension.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 11/30/23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Data { 11 | public static func makeMultiPartRequestBody(userName: String, jpegImageData: Data, boundary: String) -> Data { 12 | var body = Data() 13 | 14 | // nickname 추가 15 | body.append(Data("--\(boundary)\r\n".utf8)) 16 | body.append(Data("Content-Disposition: form-data; name=\"nickname\"\r\n\r\n".utf8)) 17 | body.append(Data(userName.utf8)) 18 | body.append(Data("\r\n".utf8)) 19 | 20 | // 이미지 데이터 추가 21 | body.append(Data("--\(boundary)\r\n".utf8)) 22 | body.append(Data("Content-Disposition: form-data; name=\"image\"; filename=\"profileImage.jpeg\"\r\n".utf8)) 23 | body.append(Data("Content-Type: image/jpeg\r\n\r\n".utf8)) 24 | body.append(jpegImageData) 25 | body.append(Data("\r\n".utf8)) 26 | body.append(Data("--\(boundary)--\r\n".utf8)) 27 | return body 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/AuthenticationEndpoints.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GoogleLoginEndpoints.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 11/23/23. 6 | // 7 | 8 | import Foundation 9 | import Network 10 | 11 | struct AuthenticationEndpoints { 12 | static func enterGoogleLogin(_ dto: GoogleAuthRequestDTO) -> EndPoint { 13 | let encoder = JSONEncoder() 14 | let data = try? encoder.encode(dto) 15 | return EndPoint(baseURL: BaseURL.flipmateDomain, path: Paths.googleApp, method: .post, data: data) 16 | } 17 | 18 | static func enterAppleLogin(_ dto: AppleAuthRequestDTO) -> EndPoint { 19 | let encoder = JSONEncoder() 20 | let data = try? encoder.encode(dto) 21 | return EndPoint(baseURL: BaseURL.flipmateDomain, path: Paths.appleApp, method: .post, data: data) 22 | } 23 | 24 | static func withdraw() -> EndPoint { 25 | return EndPoint( 26 | baseURL: BaseURL.flipmateDomain, 27 | path: Paths.auth, 28 | method: .delete) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Application/Coordinator/ChartFlow/ChartFlowCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartFlowCoordinator.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 12/5/23. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | protocol ChartFlowCoordinatorDependencies { 12 | func makeChartFlowCoordinator(navigationController: UINavigationController) -> ChartFlowCoordinator 13 | func makeChartViewController() -> UIViewController 14 | } 15 | 16 | final class ChartFlowCoordinator: Coordinator { 17 | var childCoordinators: [Coordinator] = [] 18 | private var navigationController: UINavigationController 19 | private var dependencies: ChartFlowCoordinatorDependencies 20 | 21 | init(dependencies: ChartFlowCoordinatorDependencies, navigationController: UINavigationController) { 22 | self.dependencies = dependencies 23 | self.navigationController = navigationController 24 | } 25 | 26 | func start() { 27 | let chartViewController = dependencies.makeChartViewController() 28 | navigationController.viewControllers = [chartViewController] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Category/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Category", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | .library(name: "Category", targets: ["Category"]), 11 | ], 12 | 13 | dependencies: [ 14 | .package(name: "Core", path: "../../Core"), 15 | .package(name: "DesignSystem", path: "../../DesignSystem"), 16 | .package(name: "Domain", path: "../../Domain"), 17 | .package(name: "Service", path: "../../Service") 18 | ], 19 | 20 | targets: [ 21 | .target(name: "Category", dependencies: [ 22 | .product(name: "Core", package: "Core"), 23 | .product(name: "DesignSystem", package: "DesignSystem"), 24 | .product(name: "Domain", package: "Domain"), 25 | .product(name: "CategoryService", package: "Service") 26 | ]), 27 | .testTarget(name: "CategoryTests", dependencies: ["Category"]), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /iOS/FlipMate/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - trailing_whitespace 3 | - todo 4 | - class_delegate_protocol 5 | opt_in_rules: 6 | - empty_count 7 | - force_unwrapping 8 | 9 | analyzer_rules: # rules run by `swiftlint analyze` 10 | - explicit_self 11 | 12 | included: # case-sensitive paths to include during linting. `--path` is ignored if present 13 | excluded: # case-sensitive paths to ignore during linting. Takes precedence over `included` 14 | 15 | # If true, SwiftLint will not fail if no lintable files are found. 16 | allow_zero_lintable_files: false 17 | 18 | # If true, SwiftLint will treat all warnings as errors. 19 | strict: false 20 | 21 | force_cast: warning 22 | force_try: 23 | severity: warning 24 | 25 | # 줄 길이 150으로 제한 26 | line_length: 150 27 | 28 | # 타입 네스팅 3 level에 경고 띄우기. (2 level 까지만 권장) 29 | nesting: 30 | type_level: 31 | warning: 3 32 | error: 4 33 | 34 | type_body_length: 35 | - 300 # warning 36 | - 400 # error 37 | 38 | file_length: 39 | warning: 500 40 | error: 1200 41 | 42 | type_name: 43 | min_length: 4 44 | max_length: 45 | warning: 40 46 | error: 50 47 | 48 | reporter: "xcode" 49 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Sources/Domain/UseCaseImplementations/Chart/DefaultFetchDailyChartUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultFetchDailyChartUseCase.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/21/24. 6 | // 7 | 8 | import Foundation 9 | 10 | public final class DefaultFetchDailyChartUseCase: FetchDailyChartUseCase { 11 | private let repository: ChartRepository 12 | 13 | public init(repository: ChartRepository) { 14 | self.repository = repository 15 | } 16 | 17 | public func fetchDailyChartLog(at date: Date) async throws -> CategoryChartLog { 18 | var chartLog = try await repository.fetchDailyLog(date: date) 19 | 20 | var etcTime = chartLog.studyLog.category.reduce(0) { (result, category) in 21 | return result + (category.studyTime ?? 0) 22 | } 23 | 24 | etcTime = chartLog.studyLog.totalTime - etcTime 25 | 26 | let etcCategory = StudyCategory(id: 0, color: "888888FF", subject: NSLocalizedString("etc", comment: ""), studyTime: etcTime) 27 | chartLog.studyLog.category.append(etcCategory) 28 | 29 | return chartLog 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BE/src/study-logs/dto/response/study-logs.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class StudyLogsDto { 4 | @ApiProperty({ 5 | type: 'number', 6 | example: 1, 7 | description: '학습 기록의 id', 8 | }) 9 | id: number; 10 | 11 | @ApiProperty({ 12 | type: 'date', 13 | example: '2023-11-23', 14 | description: '학습을 시작한 날짜', 15 | }) 16 | date: string; 17 | 18 | @ApiProperty({ 19 | type: 'date', 20 | example: '2023-11-23 11:00:12', 21 | description: '학습 시작/종료 시점의 시간', 22 | }) 23 | created_at: Date; 24 | 25 | @ApiProperty({ 26 | type: 'enum', 27 | example: 'start', 28 | description: '학습이 시작인지 종료인지에 대한 타입', 29 | }) 30 | type: 'start' | 'finish'; 31 | 32 | @ApiProperty({ 33 | type: 'number', 34 | example: 3600, 35 | description: '학습시간 (초 단위)', 36 | }) 37 | learning_time: number; 38 | 39 | @ApiProperty({ 40 | type: 'number', 41 | example: 1, 42 | description: '유저 아이디', 43 | }) 44 | user_id: number; 45 | 46 | @ApiProperty({ 47 | type: 'number', 48 | example: 1, 49 | description: '카테고리 아이디', 50 | }) 51 | category_id: number; 52 | } 53 | -------------------------------------------------------------------------------- /BE/src/common/config/logging.config.ts: -------------------------------------------------------------------------------- 1 | import winston from 'winston'; 2 | 3 | const customFormat = winston.format.printf( 4 | ({ level, message, timestamp, context }) => { 5 | return `[p${process.pid}]${timestamp} ${level} [${context}] ${message}`; 6 | }, 7 | ); 8 | 9 | const timestampFormat = winston.format.timestamp({ 10 | format: 'YYYY-MM-DD HH:mm:ss', 11 | }); 12 | 13 | const logFormat = winston.format.combine(timestampFormat, customFormat); 14 | 15 | const levelFilter = (level) => 16 | winston.format((info) => { 17 | return info.level === level ? info : false; 18 | })(); 19 | 20 | const fileTransport = (level) => 21 | new winston.transports.File({ 22 | filename: `logs/${level}.log`, 23 | level: 'debug', 24 | format: winston.format.combine(levelFilter(level), logFormat), 25 | }); 26 | 27 | export const loggerConfig = { 28 | transports: [ 29 | new winston.transports.Console({ 30 | level: 'debug', 31 | format: winston.format.combine(winston.format.colorize(), logFormat), 32 | }), 33 | fileTransport('debug'), 34 | fileTransport('info'), 35 | fileTransport('warn'), 36 | fileTransport('error'), 37 | ], 38 | }; 39 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Extension/UIViewController++Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController++Extension.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/12/10. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIViewController { 11 | public func showToast(title: String) { 12 | let toastLabel = UILabel(frame: CGRect( 13 | x: 40, 14 | y: self.view.frame.height / 2, 15 | width: self.view.frame.width - 80, 16 | height: 50) 17 | ) 18 | 19 | toastLabel.backgroundColor = FlipMateColor.gray4.color?.withAlphaComponent(0.5) 20 | toastLabel.textColor = .label 21 | toastLabel.textAlignment = .center 22 | toastLabel.font = FlipMateFont.smallRegular.font 23 | toastLabel.text = title 24 | toastLabel.clipsToBounds = true 25 | toastLabel.layer.cornerRadius = 10 26 | self.view.addSubview(toastLabel) 27 | 28 | UIView.animate(withDuration: 2.0, delay: 0.1) { 29 | toastLabel.alpha = 0.0 30 | } completion: { _ in 31 | toastLabel.removeFromSuperview() 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /BE/src/common/exception-filter/http-exception-filter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ArgumentsHost, 3 | Catch, 4 | ExceptionFilter, 5 | HttpException, 6 | Logger, 7 | } from '@nestjs/common'; 8 | 9 | @Catch(HttpException) 10 | export class HttpExceptionFilter implements ExceptionFilter { 11 | private readonly logger = new Logger(HttpExceptionFilter.name); 12 | catch(exception: HttpException, host: ArgumentsHost) { 13 | const ctx = host.switchToHttp(); 14 | const response = ctx.getResponse(); 15 | const request = ctx.getRequest(); 16 | const status = exception.getStatus(); 17 | 18 | const requestToResponse: `${number}ms` = request.now 19 | ? `${Date.now() - request.now}ms` 20 | : `0ms`; 21 | 22 | const errorResponse = { 23 | statusCode: exception.getResponse()['statusCode'] || status, 24 | message: exception?.message, 25 | error: exception?.getResponse()['error'], 26 | path: request.url, 27 | timestamp: new Date().toLocaleString('kr'), 28 | }; 29 | this.logger.warn( 30 | `[Exception] ${status} ${exception?.message} ${requestToResponse}`, 31 | ); 32 | 33 | response.status(status).json(errorResponse); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleURLTypes 6 | 7 | 8 | CFBundleTypeRole 9 | Editor 10 | CFBundleURLSchemes 11 | 12 | com.googleusercontent.apps.667879138413-87ugo3quu2ea9qg43gfig1il15jo6f8f 13 | 14 | 15 | 16 | GIDClientID 17 | 667879138413-87ugo3quu2ea9qg43gfig1il15jo6f8f.apps.googleusercontent.com 18 | UIApplicationSceneManifest 19 | 20 | UIApplicationSupportsMultipleScenes 21 | 22 | UISceneConfigurations 23 | 24 | UIWindowSceneSessionRoleApplication 25 | 26 | 27 | UISceneConfigurationName 28 | Default Configuration 29 | UISceneDelegateClassName 30 | $(PRODUCT_MODULE_NAME).SceneDelegate 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/TimerEndpoints.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerEndpoints.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/22. 6 | // 7 | 8 | import Foundation 9 | import Network 10 | 11 | /// Timer 기능 API 통신을 위한 Endpoint을 생성해주는 객체 12 | struct TimerEndpoints { 13 | /// Timer 시작 Endpoint을 생성해주는 메소드 14 | static func startTimer(with timerStartRequestDTO: TimerStartRequestDTO) -> EndPoint { 15 | let encoder = JSONEncoder() 16 | let data = try? encoder.encode(timerStartRequestDTO) 17 | return EndPoint( 18 | baseURL: BaseURL.flipmateDomain, 19 | path: "/study-logs", 20 | method: .post, 21 | data: data 22 | ) 23 | } 24 | /// Timer 종료 Endpoint을 생성해주는 메소드 25 | static func stopTimer(with timerFinishRequestDTO: TimerFinishRequestDTO) -> EndPoint { 26 | let encoder = JSONEncoder() 27 | let data = try? encoder.encode(timerFinishRequestDTO) 28 | return EndPoint( 29 | baseURL: BaseURL.flipmateDomain, 30 | path: "/study-logs", 31 | method: .post, 32 | data: data 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Domain/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Domain", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, making them visible to other packages. 11 | .library( 12 | name: "Domain", 13 | targets: ["Domain"]), 14 | ], 15 | dependencies: [ 16 | .package(path: "../Core"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package, defining a module or a test suite. 20 | // Targets can depend on other targets in this package and products from dependencies. 21 | .target( 22 | name: "Domain", 23 | dependencies: [ 24 | .product(name: "Core", package: "Core") 25 | ]), 26 | .testTarget( 27 | name: "DomainTests", 28 | dependencies: [ 29 | "Domain", 30 | .product(name: "Core", package: "Core") 31 | ]), 32 | ] 33 | ) 34 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Network/Tests/NetworkTests/Utils/MockURLProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 5/16/24. 6 | // 7 | 8 | import XCTest 9 | 10 | final class MockURLProtocol: URLProtocol { 11 | static var requestHandler: ((URLRequest) throws -> (HTTPURLResponse, Data))? 12 | 13 | override class func canInit(with request: URLRequest) -> Bool { 14 | true 15 | } 16 | 17 | override class func canonicalRequest(for request: URLRequest) -> URLRequest { 18 | return request 19 | } 20 | 21 | override func startLoading() { 22 | guard let handler = MockURLProtocol.requestHandler else { 23 | XCTFail("no request handler") 24 | return 25 | } 26 | 27 | do { 28 | let (response, data) = try handler(request) 29 | client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) 30 | client?.urlProtocol(self, didLoad: data) 31 | client?.urlProtocolDidFinishLoading(self) 32 | } catch { 33 | client?.urlProtocol(self, didFailWithError: error) 34 | } 35 | } 36 | 37 | override func stopLoading() {} 38 | } 39 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Network/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Network", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, making them visible to other packages. 11 | .library( 12 | name: "Network", 13 | targets: ["Network"]), 14 | ], 15 | dependencies: [ 16 | .package(path: "../Core") 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package, defining a module or a test suite. 20 | // Targets can depend on other targets in this package and products from dependencies. 21 | .target( 22 | name: "Network", 23 | dependencies: [ 24 | .product(name: "Core", package: "Core") 25 | ] 26 | ), 27 | .testTarget( 28 | name: "NetworkTests", 29 | dependencies: [ 30 | "Network", 31 | .product(name: "Core", package: "Core") 32 | ] 33 | ), 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/TabBar/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "TabBar", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, making them visible to other packages. 11 | .library( 12 | name: "TabBar", 13 | targets: ["TabBar"]), 14 | ], 15 | dependencies: [ 16 | .package(path: "../../Core"), 17 | .package(path: "../../DesignSystem") 18 | ], 19 | targets: [ 20 | // Targets are the basic building blocks of a package, defining a module or a test suite. 21 | // Targets can depend on other targets in this package and products from dependencies. 22 | .target( 23 | name: "TabBar", 24 | dependencies: [ 25 | .product(name: "Core", package: "Core"), 26 | .product(name: "DesignSystem", package: "DesignSystem") 27 | ] 28 | ), 29 | .testTarget( 30 | name: "TabBarTests", 31 | dependencies: ["TabBar"]), 32 | ] 33 | ) 34 | -------------------------------------------------------------------------------- /BE/src/common/interceptor/logging.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | NestInterceptor, 4 | ExecutionContext, 5 | CallHandler, 6 | Logger, 7 | } from '@nestjs/common'; 8 | import { Observable } from 'rxjs'; 9 | import { tap } from 'rxjs/operators'; 10 | 11 | @Injectable() 12 | export class LoggingInterceptor implements NestInterceptor { 13 | private readonly logger = new Logger(LoggingInterceptor.name); 14 | intercept(context: ExecutionContext, next: CallHandler): Observable { 15 | const ctx = context.switchToHttp(); 16 | const request = ctx.getRequest(); 17 | const response = ctx.getResponse(); 18 | const { method, url, body } = request; 19 | this.logger.debug( 20 | `[📩 Req#${request.id.slice(0, 8)}] ${method} ${url} ${request.user 21 | ?.nickname}#${request.user?.id}\n${JSON.stringify(body)}`, 22 | ); 23 | return next.handle().pipe( 24 | tap((body) => { 25 | const requestToResponse: `${number}ms` = `${ 26 | Date.now() - request.now 27 | }ms`; 28 | this.logger.debug( 29 | `[📤 Res#${request.id.slice(0, 8)}] ${ 30 | response.statusCode 31 | } ${requestToResponse} \n ${JSON.stringify(body)}`, 32 | ); 33 | }), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/SignUpEndpoints.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignUpEndpoints.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 11/28/23. 6 | // 7 | 8 | import Foundation 9 | import Network 10 | 11 | struct SignUpEndpoints { 12 | static func nickNameValidation(_ nickName: String) -> EndPoint { 13 | let path = Paths.nickNameValidation + "?nickname=\(nickName)" 14 | return EndPoint( 15 | baseURL: BaseURL.flipmateDomain, 16 | path: path, 17 | method: .get) 18 | } 19 | 20 | static func userSignUp(nickName: String, profileImageData: Data) -> EndPoint { 21 | let boundary = "\(UUID().uuidString)" 22 | let contentType = "multipart/form-data; boundary=\(boundary)" 23 | let body = Data.makeMultiPartRequestBody( 24 | userName: nickName, 25 | jpegImageData: profileImageData, 26 | boundary: boundary) 27 | 28 | return EndPoint( 29 | baseURL: BaseURL.flipmateDomain, 30 | path: Paths.authInfo, 31 | method: .patch, 32 | data: body, 33 | headers: [HTTPHeader(value: contentType, field: "Content-Type")]) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_Dark_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "FlipMate_icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "idiom" : "universal", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "filename" : "FlipMate_icon@2x.png", 20 | "idiom" : "universal", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "appearances" : [ 25 | { 26 | "appearance" : "luminosity", 27 | "value" : "dark" 28 | } 29 | ], 30 | "idiom" : "universal", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "filename" : "FlipMate_icon@3x.png", 35 | "idiom" : "universal", 36 | "scale" : "3x" 37 | }, 38 | { 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "idiom" : "universal", 46 | "scale" : "3x" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Repositories/DefaultProfileSettingsRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultSignUpRepository.swift 3 | // FlipMate 4 | // 5 | // Created by 권승용 on 11/28/23. 6 | // 7 | 8 | import Foundation 9 | 10 | import Domain 11 | import Network 12 | import Core 13 | 14 | public final class DefaultProfileSettingsRepository: ProfileSettingsRepository { 15 | private let provider: Providable 16 | 17 | public init(provider: Providable) { 18 | self.provider = provider 19 | } 20 | 21 | public func checkIfNickNameIsDuplicated(_ nickName: String) async throws -> Bool { 22 | let endpoint = SignUpEndpoints.nickNameValidation(nickName) 23 | let response = try await provider.request(with: endpoint) 24 | return !response.isUnique 25 | } 26 | 27 | public func setupNewProfileInfo(nickName: String, profileImageData: Data) async throws -> UserInfo { 28 | let endpoint = SignUpEndpoints.userSignUp(nickName: nickName, profileImageData: profileImageData) 29 | let response = try await provider.request(with: endpoint) 30 | FMLogger.general.log("유저 정보 설정 완료 : \(response.nickName)") 31 | return UserInfo(name: response.nickName, profileImageURL: response.imageURL, email: response.email) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Buttons/DoneButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by 권승용 on 6/2/24. 6 | // 7 | 8 | import UIKit 9 | 10 | public final class DoneButton: UIButton { 11 | public enum ButtonState { 12 | case normal 13 | case disabled 14 | } 15 | 16 | private var disabledBackgroundColor: UIColor? 17 | private var defaultBackgroundColor: UIColor? { 18 | didSet { 19 | backgroundColor = defaultBackgroundColor 20 | } 21 | } 22 | 23 | public override var isEnabled: Bool { 24 | didSet { 25 | if isEnabled { 26 | if let color = defaultBackgroundColor { 27 | self.backgroundColor = color 28 | } 29 | } else { 30 | if let color = disabledBackgroundColor { 31 | self.backgroundColor = color 32 | } 33 | } 34 | } 35 | } 36 | 37 | public func setBackgroundColor(_ color: UIColor?, for state: ButtonState) { 38 | switch state { 39 | case .disabled: 40 | disabledBackgroundColor = color 41 | case .normal: 42 | defaultBackgroundColor = color 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Application/DIContainer/AppDIContainer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDIContainer.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/25. 6 | // 7 | 8 | import UIKit 9 | import Core 10 | import Network 11 | import Timer 12 | import CategoryService 13 | 14 | final class AppDIContainer { 15 | lazy var provider: Provider = Provider(urlSession: URLSession.shared, keychainManager: KeychainManager()) 16 | lazy var categoryManager: CategoryManageable = CategoryManager(categories: []) 17 | lazy var userInfoManager: UserInfoManageable = UserInfoManager() 18 | 19 | func makeTabBarDIContainer() -> TabBarDIContainer { 20 | let dependencies = TabBarDIContainer.Dependencies( 21 | provider: provider, 22 | categoryManager: categoryManager, 23 | userInfoManager: userInfoManager 24 | ) 25 | 26 | return TabBarDIContainer(dependencies: dependencies) 27 | } 28 | 29 | func makeLoginDiContainer() -> LoginDIContainer { 30 | let dependencies = LoginDIContainer.Dependencies( 31 | provider: provider, 32 | categoryManager: categoryManager, 33 | userInfoManager: userInfoManager 34 | ) 35 | 36 | return LoginDIContainer(dependencies: dependencies) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Repositories/DefaultUserInfoRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultUserInfoRepository.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/12/04. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | import Domain 12 | import Network 13 | import Core 14 | 15 | public final class DefaultUserInfoRepository: UserInfoRepository { 16 | private let provider: Providable 17 | 18 | public init(provider: Providable) { 19 | self.provider = provider 20 | } 21 | 22 | public func getUserInfo() -> AnyPublisher { 23 | let endpoint = UserInfoEndpoints.userInfo() 24 | return provider.request(with: endpoint) 25 | .map { response -> UserInfo in 26 | return UserInfo( 27 | name: response.nickname, 28 | profileImageURL: response.imageURL, 29 | email: response.email) 30 | } 31 | .eraseToAnyPublisher() 32 | } 33 | 34 | public func patchTimeZone(date: Date) async throws { 35 | let reqeust = TimeZoneRequestDTO(timezone: date.dateToString(format: .ZZZZZ)) 36 | let endpoint = UserInfoEndpoints.patchTimeZone(with: reqeust) 37 | _ = try await provider.request(with: endpoint) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /BE/test/load-performance/loadtest-social.js: -------------------------------------------------------------------------------- 1 | import { SharedArray } from 'k6/data'; 2 | import http from 'k6/http'; 3 | import { check, sleep } from 'k6'; 4 | import moment from 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js'; 5 | import faker from 'https://cdnjs.cloudflare.com/ajax/libs/Faker/3.1.0/faker.min.js'; 6 | 7 | const tokens = new SharedArray('possible tokens', function () { 8 | const mock_users = JSON.parse(open('./loadtest-mock.json')); 9 | return mock_users.map(({ access_token }) => access_token); 10 | }); 11 | 12 | export let options = { 13 | stages: [ 14 | { duration: '30s', target: 50 }, 15 | { duration: '20s', target: 0 }, 16 | ], 17 | thresholds: { 18 | http_req_duration: ['avg<200', 'p(95)<500', 'max<1000'], 19 | http_reqs: ['rate>100'], 20 | }, 21 | }; 22 | 23 | export default function () { 24 | const token = tokens[__VU % tokens.length]; 25 | const url = 'http://localhost:3000/mates'; 26 | const params = { 27 | headers: { 28 | Authorization: `Bearer ${token}`, 29 | 'Content-Type': 'application/json', 30 | }, 31 | }; 32 | 33 | const date = moment(faker.date.between('2023-11-23', '2023-11-30')); 34 | check(http.get(`${url}?date=${date}`, params), { 35 | '친구 조회 응답 시간이 1초 이내': (r) => r.timings.duration < 1000, 36 | }); 37 | sleep(3); 38 | } 39 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/Chart/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Chart", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, making them visible to other packages. 11 | .library( 12 | name: "Chart", 13 | targets: ["Chart"]), 14 | ], 15 | dependencies: [ 16 | .package(path: "../../Core"), 17 | .package(path: "../../Domain"), 18 | .package(path: "../../DesignSystem") 19 | ], 20 | targets: [ 21 | // Targets are the basic building blocks of a package, defining a module or a test suite. 22 | // Targets can depend on other targets in this package and products from dependencies. 23 | .target( 24 | name: "Chart", 25 | dependencies: [ 26 | .product(name: "Core", package: "Core"), 27 | .product(name: "Domain", package: "Domain"), 28 | .product(name: "DesignSystem", package: "DesignSystem") 29 | ] 30 | ), 31 | .testTarget( 32 | name: "ChartTests", 33 | dependencies: ["Chart"]), 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Data/Sources/Data/Network/DataMapping/TimerScene/StudyLogResponseDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudyLogResponseDTO.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/23. 6 | // 7 | 8 | import Foundation 9 | 10 | import Domain 11 | 12 | struct StudyLogResponseDTO: Decodable { 13 | let todayTime: Int 14 | let categories: [CategoryDTO]? 15 | 16 | private enum CodingKeys: String, CodingKey { 17 | case todayTime = "total_time" 18 | case categories 19 | } 20 | 21 | func toEntity() -> StudyLog { 22 | guard let categories = categories else { return .init(totalTime: todayTime, category: []) } 23 | return .init( 24 | totalTime: todayTime, 25 | category: categories.map { 26 | StudyCategory(id: $0.id, color: $0.color, subject: $0.name, studyTime: $0.todayTime) 27 | } 28 | ) 29 | } 30 | } 31 | 32 | extension StudyLogResponseDTO { 33 | struct CategoryDTO: Decodable { 34 | let id: Int 35 | let name: String 36 | let color: String 37 | let todayTime: Int 38 | 39 | private enum CodingKeys: String, CodingKey { 40 | case id 41 | case name 42 | case color = "color_code" 43 | case todayTime = "today_time" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Timer/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Timer", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, making them visible to other packages. 11 | .library( 12 | name: "Timer", 13 | targets: ["Timer"]), 14 | ], 15 | dependencies: [ 16 | .package(path: "../../Core"), 17 | .package(path: "../../Domain"), 18 | .package(path: "../../Service") 19 | ], 20 | targets: [ 21 | // Targets are the basic building blocks of a package, defining a module or a test suite. 22 | // Targets can depend on other targets in this package and products from dependencies. 23 | .target( 24 | name: "Timer", 25 | dependencies: [ 26 | .product(name: "Core", package: "Core"), 27 | .product(name: "Domain", package: "Domain"), 28 | .product(name: "CategoryService", package: "Service") 29 | 30 | ]), 31 | .testTarget( 32 | name: "TimerTests", 33 | dependencies: ["Timer"]), 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /iOS/FlipMate/Feature/MyPage/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "MyPage", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, making them visible to other packages. 11 | .library( 12 | name: "MyPage", 13 | targets: ["MyPage"]), 14 | ], 15 | dependencies: [ 16 | .package(path: "../../Core"), 17 | .package(path: "../../Domain"), 18 | .package(path: "../../DesignSystem") 19 | ], 20 | targets: [ 21 | // Targets are the basic building blocks of a package, defining a module or a test suite. 22 | // Targets can depend on other targets in this package and products from dependencies. 23 | .target( 24 | name: "MyPage", 25 | dependencies: [ 26 | .product(name: "Core", package: "Core"), 27 | .product(name: "Domain", package: "Domain"), 28 | .product(name: "DesignSystem", package: "DesignSystem") 29 | ] 30 | ), 31 | .testTarget( 32 | name: "MyPageTests", 33 | dependencies: ["MyPage"]), 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Social/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Social", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, making them visible to other packages. 11 | .library( 12 | name: "Social", 13 | targets: ["Social"]), 14 | ], 15 | dependencies: [ 16 | .package(path: "../../Core"), 17 | .package(path: "../../Domain"), 18 | .package(path: "../../DesignSystem") 19 | ], 20 | targets: [ 21 | // Targets are the basic building blocks of a package, defining a module or a test suite. 22 | // Targets can depend on other targets in this package and products from dependencies. 23 | .target( 24 | name: "Social", 25 | dependencies: [ 26 | .product(name: "Core", package: "Core"), 27 | .product(name: "Domain", package: "Domain"), 28 | .product(name: "DesignSystem", package: "DesignSystem") 29 | ] 30 | ), 31 | .testTarget( 32 | name: "SocialTests", 33 | dependencies: ["Social"]), 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Font/FlipMateFont.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlipMateFont.swift 3 | // FlipMate 4 | // 5 | // Created by 임현규 on 2023/11/13. 6 | // 7 | 8 | import UIKit 9 | 10 | public enum FlipMateFont { 11 | case largeRegular 12 | case semiLargeRegular 13 | case mediumRegular 14 | case smallRegular 15 | case caption 16 | case extraLargeBold 17 | case largeBold 18 | case semiLargeBold 19 | case mediumBold 20 | case smallBold 21 | 22 | public var font: UIFont { 23 | switch self { 24 | case .largeRegular: return .systemFont(ofSize: 32, weight: .regular) 25 | case .semiLargeRegular: return .systemFont(ofSize: 24, weight: .regular) 26 | case .mediumRegular: return .systemFont(ofSize: 18, weight: .regular) 27 | case .smallRegular: return .systemFont(ofSize: 14, weight: .regular) 28 | case .caption: return .systemFont(ofSize: 12, weight: .regular) 29 | case .extraLargeBold: return .systemFont(ofSize: 58, weight: .bold) 30 | case .largeBold: return .systemFont(ofSize: 32, weight: .bold) 31 | case .semiLargeBold: return .systemFont(ofSize: 24, weight: .bold) 32 | case .mediumBold: return .systemFont(ofSize: 18, weight: .bold) 33 | case .smallBold: return .systemFont(ofSize: 14, weight: .bold) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /BE/test/mock-table/categories.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "백준", 5 | "color_code": "FFFFFF", 6 | "user_id": 1 7 | }, 8 | { 9 | "id": 2, 10 | "name": "과학", 11 | "color_code": "BBBBBB", 12 | "user_id": 1 13 | }, 14 | { 15 | "id": 3, 16 | "name": "수학", 17 | "color_code": "CCCCCC", 18 | "user_id": 3 19 | }, 20 | { 21 | "id": 4, 22 | "name": "영어", 23 | "color_code": "DDDDDD", 24 | "user_id": 3 25 | }, 26 | { 27 | "id": 5, 28 | "name": "국어", 29 | "color_code": "EEEEEE", 30 | "user_id": 3 31 | }, 32 | { 33 | "id": 6, 34 | "name": "사회", 35 | "color_code": "FFFFFF", 36 | "user_id": 3 37 | }, 38 | { 39 | "id": 7, 40 | "name": "과학", 41 | "color_code": "FFFFFF", 42 | "user_id": 3 43 | }, 44 | { 45 | "id": 8, 46 | "name": "수학", 47 | "color_code": "CCCCCC", 48 | "user_id": 3 49 | }, 50 | { 51 | "id": 9, 52 | "name": "영어", 53 | "color_code": "DDDDDD", 54 | "user_id": 3 55 | }, 56 | { 57 | "id": 10, 58 | "name": "국어", 59 | "color_code": "EEEEEE", 60 | "user_id": 3 61 | }, 62 | { 63 | "id": 11, 64 | "name": "사회", 65 | "color_code": "FFFFFF", 66 | "user_id": 3 67 | }, 68 | { 69 | "id": 12, 70 | "name": "과학", 71 | "color_code": "FFFFFF", 72 | "user_id": 3 73 | } 74 | ] 75 | -------------------------------------------------------------------------------- /BE/src/categories/categories.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | ManyToOne, 6 | OneToMany, 7 | JoinColumn, 8 | } from 'typeorm'; 9 | import { ApiProperty } from '@nestjs/swagger'; 10 | import { UsersModel } from 'src/users/entity/users.entity'; 11 | import { StudyLogs } from 'src/study-logs/study-logs.entity'; 12 | import { IsString, Length, Matches } from 'class-validator'; 13 | 14 | @Entity() 15 | export class Categories { 16 | @PrimaryGeneratedColumn() 17 | id: number; 18 | 19 | @ApiProperty({ 20 | type: 'string', 21 | example: '백준', 22 | description: '카테고리 이름', 23 | }) 24 | @Column({ 25 | type: 'char', 26 | length: 50, 27 | }) 28 | @IsString() 29 | @Length(1, 50) 30 | name: string; 31 | 32 | @ApiProperty({ 33 | type: 'string', 34 | example: 'FFFFFFFF', 35 | description: '카테고리 색상', 36 | }) 37 | @Column({ 38 | type: 'char', 39 | length: 8, 40 | }) 41 | @IsString() 42 | @Length(1, 8) 43 | @Matches(/^[0-9A-F]{1,8}$/i, { message: '올바른 색상 코드가 아닙니다.' }) 44 | color_code: string; 45 | 46 | @ManyToOne(() => UsersModel, (user) => user.categories, { 47 | eager: true, 48 | onDelete: 'CASCADE', 49 | }) 50 | @JoinColumn({ name: 'user_id' }) 51 | user_id: UsersModel; 52 | 53 | @OneToMany(() => StudyLogs, (studyLog) => studyLog.category_id) 54 | study_logs: StudyLogs[]; 55 | } 56 | -------------------------------------------------------------------------------- /BE/test/mock-repo/mock-categories-repo.ts: -------------------------------------------------------------------------------- 1 | import categoriesData from '../mock-table/categories.json'; 2 | 3 | export class MockCategoriesRepository { 4 | private data = categoriesData; 5 | create(entity: object): object { 6 | return { 7 | id: this.data.length + 1, 8 | ...entity, 9 | }; 10 | } 11 | 12 | save(entity: object): Promise { 13 | return Promise.resolve(entity); 14 | } 15 | 16 | find({ where: { user_id } }): Promise { 17 | const categories = this.data.filter( 18 | (category) => category.user_id === user_id.id, 19 | ); 20 | return Promise.resolve(categories); 21 | } 22 | 23 | findOne({ where: { id } }): Promise { 24 | const category = this.data.find((category) => category.id === id); 25 | if (!category) { 26 | return Promise.resolve(null); 27 | } 28 | const result = { 29 | ...category, 30 | user_id: { id: category.user_id }, 31 | }; 32 | return Promise.resolve(result); 33 | } 34 | 35 | delete(id): Promise { 36 | const category = this.data.find((category) => category.id === id); 37 | return Promise.resolve(category); 38 | } 39 | 40 | count({ where: { user_id } }): Promise { 41 | const categories = this.data.filter( 42 | (category) => category.user_id === user_id.id, 43 | ); 44 | return Promise.resolve(categories.length); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/Application/DIContainer/ChartDIContainer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartDIContainer.swift 3 | // FlipMate 4 | // 5 | // Created by 신민규 on 12/5/23. 6 | // 7 | 8 | import UIKit 9 | import Chart 10 | import Domain 11 | import Network 12 | import Data 13 | 14 | final class ChartDIContainer: ChartFlowCoordinatorDependencies { 15 | struct Dependencies { 16 | let provider: Providable 17 | } 18 | 19 | private let dependencies: Dependencies 20 | 21 | init(dependencies: Dependencies) { 22 | self.dependencies = dependencies 23 | } 24 | 25 | func makeChartFlowCoordinator(navigationController: UINavigationController) -> ChartFlowCoordinator { 26 | return ChartFlowCoordinator( 27 | dependencies: self, 28 | navigationController: navigationController) 29 | } 30 | 31 | func makeChartViewController() -> UIViewController { 32 | let repository = DefaultChartRepository(provider: dependencies.provider) 33 | return ChartViewController( 34 | viewModel: ChartViewModel( 35 | dailyChartUseCase: DefaultFetchDailyChartUseCase( 36 | repository: repository 37 | ), 38 | weeklyChartUseCase: DefaultFetchWeeklyChartUseCase( 39 | repository: repository 40 | ) 41 | ) 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /BE/src/common/s3.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, InternalServerErrorException } from '@nestjs/common'; 2 | import { ConfigService } from '@nestjs/config'; 3 | import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; 4 | import sharp from 'sharp'; 5 | import { v4 as uuidv4 } from 'uuid'; 6 | import path from 'path'; 7 | 8 | @Injectable() 9 | export class S3Service { 10 | private s3Client: S3Client; 11 | 12 | constructor(private configService: ConfigService) { 13 | this.s3Client = new S3Client({ 14 | endpoint: this.configService.get('IMAGE_ENDPOINT'), 15 | credentials: { 16 | accessKeyId: this.configService.get('IMAGE_ACCESSKEY'), 17 | secretAccessKey: this.configService.get('IMAGE_SECRETKEY'), 18 | }, 19 | region: this.configService.get('IMAGE_REGION'), 20 | }); 21 | } 22 | 23 | async uploadFile(file: Express.Multer.File): Promise { 24 | try { 25 | const buffer = await sharp(file.buffer).resize(800, 800).toBuffer(); 26 | const fileName = `IMG_${uuidv4()}${path.extname(file.originalname)}`; 27 | 28 | await this.s3Client.send( 29 | new PutObjectCommand({ 30 | Bucket: this.configService.get('IMAGE_BUCKET'), 31 | Key: fileName, 32 | Body: buffer, 33 | }), 34 | ); 35 | 36 | return fileName; 37 | } catch (error) { 38 | throw new InternalServerErrorException(error); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/FlipMate_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "FlipMate_icon_dark.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "FlipMate_icon 1.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "FlipMate_icon_dark@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "FlipMate_icon@2x.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "FlipMate_icon_dark@3x.png", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "FlipMate_icon@3x 1.png", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/instruction_EN.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Instruction_EN_Light.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "Instruction_EN_Dark.svg", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "Instruction_EN_Light 1.svg", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "Instruction_EN_Dark 1.svg", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "Instruction_EN_Light 2.svg", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "Instruction_EN_Dark 2.svg", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /iOS/FlipMate/FlipMate/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/instruction_JP.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Instruction_JP_Light.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "Instruction_JP_Dark.svg", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "Instruction_JP_Light 1.svg", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "Instruction_JP_Dark 1.svg", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "Instruction_JP_Light 2.svg", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "Instruction_JP_Dark 2.svg", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | --------------------------------------------------------------------------------