├── .github ├── CODEOWNERS ├── labeler.yml ├── pull-request-template.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── auto-author-assign.yml │ ├── auto-label-in-issue.yml │ ├── add-to-project.yml │ ├── label.yml │ ├── build-alpha.yml │ └── build.yml ├── frontend ├── .dockerignore ├── public │ ├── locales │ │ ├── ko │ │ │ ├── 500.json │ │ │ ├── header.json │ │ │ ├── footer.json │ │ │ ├── 404.json │ │ │ ├── tier.json │ │ │ ├── index.json │ │ │ ├── ranking.json │ │ │ ├── common.json │ │ │ ├── meta.json │ │ │ ├── profile.json │ │ │ └── about.json │ │ └── en │ │ │ ├── header.json │ │ │ ├── 500.json │ │ │ ├── footer.json │ │ │ ├── tier.json │ │ │ ├── 404.json │ │ │ ├── index.json │ │ │ ├── ranking.json │ │ │ ├── common.json │ │ │ ├── meta.json │ │ │ ├── profile.json │ │ │ └── about.json │ ├── favicon.ico │ ├── og-thumbnail.png │ ├── profile-dummy.png │ ├── robots.txt │ ├── fonts │ │ ├── line-seed-kr-bold.woff2 │ │ ├── line-seed-kr-thin.woff2 │ │ ├── line-seed-kr-regular.woff │ │ └── line-seed-kr-regular.woff2 │ └── icons │ │ ├── arrow-up.svg │ │ ├── arrow-bar.svg │ │ ├── refresh.svg │ │ ├── link.svg │ │ ├── search.svg │ │ ├── profile.svg │ │ ├── company.svg │ │ ├── ranking-1st.svg │ │ ├── ranking-2nd.svg │ │ ├── ranking-3rd.svg │ │ ├── logout.svg │ │ ├── github.svg │ │ ├── location.svg │ │ ├── star.svg │ │ ├── globe.svg │ │ ├── fork.svg │ │ ├── organization.svg │ │ ├── username.svg │ │ └── people.svg ├── src │ ├── components │ │ ├── common │ │ │ ├── Skeleton │ │ │ │ ├── index.ts │ │ │ │ ├── RankingSkeleton.stories.tsx │ │ │ │ └── RankingSkeleton.tsx │ │ │ ├── index.ts │ │ │ ├── CubeIcon │ │ │ │ ├── index.tsx │ │ │ │ └── CubeIcon.stories.tsx │ │ │ ├── Paper │ │ │ │ ├── Paper.stories.tsx │ │ │ │ └── index.tsx │ │ │ ├── Spinner │ │ │ │ ├── Spinner.stories.tsx │ │ │ │ └── index.tsx │ │ │ ├── LanguageIcon │ │ │ │ ├── LanguageIcon.stories.tsx │ │ │ │ └── index.tsx │ │ │ ├── Button │ │ │ │ ├── Button.stories.tsx │ │ │ │ └── index.tsx │ │ │ ├── Searchbar │ │ │ │ ├── Searchbar.stories.tsx │ │ │ │ ├── AutoComplete │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── AutoComplete.tsx │ │ │ │ └── index.tsx │ │ │ ├── HeadMeta │ │ │ │ └── index.tsx │ │ │ ├── Avatar │ │ │ │ ├── Avatar.stories.tsx │ │ │ │ └── index.tsx │ │ │ └── Dropdown │ │ │ │ ├── Dropdown.stories.tsx │ │ │ │ └── index.tsx │ │ ├── Chart │ │ │ ├── index.ts │ │ │ ├── Tooltip │ │ │ │ └── index.tsx │ │ │ ├── theme.json │ │ │ ├── PieChart │ │ │ │ └── index.tsx │ │ │ └── LineChart │ │ │ │ └── index.tsx │ │ ├── Profile │ │ │ ├── index.ts │ │ │ ├── ProfileLabel │ │ │ │ ├── ProfileLabel.stories.tsx │ │ │ │ └── index.tsx │ │ │ ├── EXPbar │ │ │ │ ├── EXPbar.stories.tsx │ │ │ │ └── index.tsx │ │ │ └── ProfileRefreshButton │ │ │ │ └── index.tsx │ │ ├── Footer │ │ │ ├── Footer.stories.tsx │ │ │ └── index.tsx │ │ ├── Pagination │ │ │ ├── Arrow.stories.tsx │ │ │ ├── Pagination.stories.tsx │ │ │ ├── index.tsx │ │ │ └── Arrow.tsx │ │ ├── Ranking │ │ │ ├── index.ts │ │ │ ├── RankingSearchbar │ │ │ │ └── index.tsx │ │ │ ├── NotFound │ │ │ │ └── index.tsx │ │ │ ├── RankingTable │ │ │ │ └── RankingTable.stories.tsx │ │ │ ├── LanguageRanking │ │ │ │ └── index.tsx │ │ │ ├── ViewsRanking │ │ │ │ └── index.tsx │ │ │ ├── OverallRanking │ │ │ │ └── index.tsx │ │ │ └── RisingRanking │ │ │ │ └── index.tsx │ │ ├── Filterbar │ │ │ ├── Filterbar.stories.tsx │ │ │ └── index.tsx │ │ └── Layout │ │ │ └── index.tsx │ ├── hooks │ │ ├── index.ts │ │ ├── useDropdown.ts │ │ ├── useInterval.ts │ │ ├── useInput.ts │ │ ├── useDebounce.ts │ │ ├── useQueryData.ts │ │ └── useAutoComplete.ts │ ├── contexts │ │ └── dropdown.tsx │ ├── styles │ │ ├── styled.d.ts │ │ ├── globalStyles.ts │ │ └── theme.ts │ ├── utils │ │ ├── fonts.ts │ │ └── axiosInstance.ts │ ├── pages │ │ ├── sitemap.tsx │ │ ├── _document.tsx │ │ ├── callback.tsx │ │ ├── _app.tsx │ │ ├── 404.tsx │ │ ├── _error.tsx │ │ ├── profile │ │ │ └── 404.tsx │ │ └── api │ │ │ └── og-image.tsx │ ├── apis │ │ ├── users.ts │ │ ├── auth.ts │ │ └── ranking.ts │ └── type │ │ ├── common.ts │ │ └── response.ts ├── next-sitemap.config.js ├── Dockerfile ├── next-i18next.config.js ├── .storybook │ ├── i18n.js │ ├── preview.js │ └── main.js ├── .gitignore ├── .prettierrc ├── tsconfig.json ├── next.config.js ├── README.md ├── .eslintrc.js └── package.json ├── backend ├── libs │ ├── utils │ │ ├── index.ts │ │ ├── logger.ts │ │ └── utils.ts │ ├── common │ │ ├── decorators │ │ │ ├── current-user.decodator.ts │ │ │ ├── user-github-id.decorator.ts │ │ │ └── user-github-token.decorator.ts │ │ ├── middlewares │ │ │ └── logger.middleware.ts │ │ └── filters │ │ │ └── http-exception.filter.ts │ └── consts │ │ └── index.ts ├── .dockerignore ├── src │ ├── auth │ │ ├── types │ │ │ ├── index.ts │ │ │ ├── payload.interface.ts │ │ │ └── github-profile.interface.ts │ │ ├── dto │ │ │ ├── login-request.dto.ts │ │ │ └── login-response-dto.ts │ │ ├── guards │ │ │ ├── refresh-auth.guard.ts │ │ │ └── jwt-auth.guard.ts │ │ ├── auth.repository.ts │ │ ├── strategies │ │ │ ├── jwt.strategy.ts │ │ │ └── refresh.strategy.ts │ │ └── auth.module.ts │ ├── user │ │ ├── dto │ │ │ ├── organization.dto.ts │ │ │ ├── user.profile.dto.ts │ │ │ ├── pinned-repository.dto.ts │ │ │ ├── auto-complete.dto.ts │ │ │ ├── history.dto.ts │ │ │ └── user.dto.ts │ │ ├── user.module.ts │ │ └── user.schema.ts │ ├── app.controller.ts │ ├── batch │ │ ├── batch.module.ts │ │ └── batch.service.ts │ ├── ranking │ │ ├── dto │ │ │ ├── ranking-language.dto.ts │ │ │ ├── ranking-pagination.dto.ts │ │ │ └── ranking-user.dto.ts │ │ ├── ranking.module.ts │ │ └── ranking.service.ts │ ├── app.service.ts │ ├── main.ts │ └── app.module.ts ├── tsconfig.build.json ├── nest-cli.json ├── test │ ├── jest-e2e.json │ └── app.e2e-spec.ts ├── sample.env ├── Dockerfile ├── .prettierrc ├── .gitignore ├── tsconfig.json └── .eslintrc.js ├── infra ├── hooks.service ├── renew.sh ├── hooks.json ├── initial-deploy.sh ├── nginx │ └── default.conf ├── docker-compose.yml ├── README.md ├── backend-deploy.sh └── frontend-deploy.sh └── .gitignore /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @boostcampwm-2022/team 2 | -------------------------------------------------------------------------------- /frontend/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | node_modules 4 | .next -------------------------------------------------------------------------------- /backend/libs/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './logger'; 2 | export * from './utils'; 3 | -------------------------------------------------------------------------------- /backend/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | Dockerfile 3 | docker-compose.yml 4 | .git 5 | .idea 6 | -------------------------------------------------------------------------------- /frontend/public/locales/ko/500.json: -------------------------------------------------------------------------------- 1 | { 2 | "500-information": "예기치 않은 오류가 발생했습니다. 조금 있다가 다시 시도해주세요." 3 | } 4 | -------------------------------------------------------------------------------- /frontend/public/locales/ko/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigate-about": "소개", 3 | "navigate-ranking": "랭킹" 4 | } 5 | -------------------------------------------------------------------------------- /backend/src/auth/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './github-profile.interface'; 2 | export * from './payload.interface'; 3 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/web21-devrank/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/locales/en/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigate-about": "Intro", 3 | "navigate-ranking": "Leaderboard" 4 | } 5 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | ':gear: backend': 2 | - any: ['backend/**/*'] 3 | 4 | ':art: frontend': 5 | - any: ['frontend/**/*'] 6 | -------------------------------------------------------------------------------- /frontend/public/locales/en/500.json: -------------------------------------------------------------------------------- 1 | { 2 | "404-information": "An unexpected error has occurred. Please try again later." 3 | } 4 | -------------------------------------------------------------------------------- /frontend/public/og-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/web21-devrank/HEAD/frontend/public/og-thumbnail.png -------------------------------------------------------------------------------- /frontend/public/profile-dummy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/web21-devrank/HEAD/frontend/public/profile-dummy.png -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /callback/ 3 | Disallow: /404/ 4 | Sitemap: https://dreamdev.me/sitemap.xml -------------------------------------------------------------------------------- /frontend/src/components/common/Skeleton/index.ts: -------------------------------------------------------------------------------- 1 | import RankingSkeleton from './RankingSkeleton'; 2 | 3 | export { RankingSkeleton }; 4 | -------------------------------------------------------------------------------- /backend/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /frontend/public/fonts/line-seed-kr-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/web21-devrank/HEAD/frontend/public/fonts/line-seed-kr-bold.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/line-seed-kr-thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/web21-devrank/HEAD/frontend/public/fonts/line-seed-kr-thin.woff2 -------------------------------------------------------------------------------- /.github/pull-request-template.md: -------------------------------------------------------------------------------- 1 | # :eyes: What is this PR? 2 | 3 | # :pushpin: Related issue(s) 4 | 5 | # :pencil: Changes 6 | 7 | # :camera: Attachment 8 | -------------------------------------------------------------------------------- /backend/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src" 5 | } 6 | -------------------------------------------------------------------------------- /frontend/public/fonts/line-seed-kr-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/web21-devrank/HEAD/frontend/public/fonts/line-seed-kr-regular.woff -------------------------------------------------------------------------------- /frontend/public/fonts/line-seed-kr-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/web21-devrank/HEAD/frontend/public/fonts/line-seed-kr-regular.woff2 -------------------------------------------------------------------------------- /frontend/public/locales/en/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "copyright": "Copyright ⓒ 2022 Devrank All rights reserved", 3 | "contribute": "Contribute to this project" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/public/locales/ko/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "copyright": "Copyright ⓒ 2022 Devrank All rights reserved", 3 | "contribute": "Contribute to this project" 4 | } 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Bug issue 4 | title: "[Bug] " 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | # 📘 설명 11 | 12 | # 📗 세부사항 13 | -------------------------------------------------------------------------------- /frontend/src/components/Chart/index.ts: -------------------------------------------------------------------------------- 1 | import LineChart from './LineChart'; 2 | import PieChart from './PieChart'; 3 | import Tooltip from './Tooltip'; 4 | 5 | export { LineChart, PieChart, Tooltip }; 6 | -------------------------------------------------------------------------------- /frontend/next-sitemap.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next-sitemap').IConfig} */ 2 | 3 | module.exports = { 4 | siteUrl: process.env.SITE_URL || 'https://example.com', 5 | generateRobotsTxt: true, 6 | }; 7 | -------------------------------------------------------------------------------- /frontend/public/locales/ko/404.json: -------------------------------------------------------------------------------- 1 | { 2 | "404-information": "찾고 있는 페이지가 없어졌거나 일시적으로 사용할 수 없습니다.", 3 | "user-not-found": "Github에 등록되지 않은 사용자입니다.", 4 | "check-username": "유저의 이름을 확인 후 다시 검색해주세요" 5 | } 6 | -------------------------------------------------------------------------------- /backend/src/auth/types/payload.interface.ts: -------------------------------------------------------------------------------- 1 | import { JwtPayload } from 'jsonwebtoken'; 2 | 3 | export interface Payload extends JwtPayload { 4 | id: string; 5 | githubToken: string; 6 | refreshToken?: string; 7 | } 8 | -------------------------------------------------------------------------------- /frontend/public/locales/ko/tier.json: -------------------------------------------------------------------------------- 1 | { 2 | "all": "모든 등급", 3 | "yellow": "옐로우", 4 | "green": "그린", 5 | "mint": "민트", 6 | "blue": "블루", 7 | "purple": "퍼플", 8 | "orange": "오렌지", 9 | "red": "레드" 10 | } 11 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-alpine3.14 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package.json . 6 | COPY yarn.lock . 7 | 8 | RUN yarn install 9 | 10 | COPY . . 11 | 12 | RUN yarn build 13 | 14 | CMD ["yarn", "start"] -------------------------------------------------------------------------------- /backend/src/auth/dto/login-request.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsString } from 'class-validator'; 3 | 4 | export class LoginRequestDto { 5 | @IsString() 6 | @ApiProperty() 7 | code: string; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/public/locales/en/tier.json: -------------------------------------------------------------------------------- 1 | { 2 | "all": "All", 3 | "yellow": "Yellow", 4 | "green": "Green", 5 | "mint": "Mint", 6 | "blue": "Blue", 7 | "purple": "Purple", 8 | "orange": "Orange", 9 | "red": "Red" 10 | } 11 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/sample.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=developmentOrProduction 2 | DB_URL=my_db_rul 3 | CLIENT_URL=client_app_url 4 | JWT_ACCESS_SECRET=secret 5 | JWT_ACCESS_EXPIRATION=1s 6 | JWT_REFRESH_SECRET=secret 7 | JWT_REFRESH_EXPIRATION=1d 8 | REFRESH_TOKEN_KEY=refreshKeyName 9 | -------------------------------------------------------------------------------- /frontend/public/icons/arrow-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/locales/ko/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "search-placeholder": "유저를 검색해주세요", 3 | "overall-ranking": "전체 순위", 4 | "rising-user-top-3": "급상승 유저 TOP3", 5 | "daily-views-top-3": "일일 조회수 TOP3", 6 | "most-programming-lang-top-3": "가장 많이 사용하는 언어 TOP3" 7 | } 8 | -------------------------------------------------------------------------------- /frontend/next-i18next.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | i18n: { 5 | // 어플리케이션에서 지원할 언어 리스트 6 | locales: ['en', 'ko'], 7 | defaultLocale: 'ko', 8 | }, 9 | localePath: path.resolve('./public/locales'), 10 | }; 11 | -------------------------------------------------------------------------------- /backend/src/user/dto/organization.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class OrganizationDto { 4 | @ApiProperty() 5 | name: string; 6 | 7 | @ApiProperty() 8 | avatarUrl: string; 9 | 10 | @ApiProperty() 11 | url: string; 12 | } 13 | -------------------------------------------------------------------------------- /infra/hooks.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=backend-auto-deploy-endpoint 3 | 4 | [Service] 5 | ExecStart=/usr/bin/webhook -hooks {{ HOOKS_FILE_PATH }} -port {{ PORT }} --verbose 6 | User={{ USERNAME }} 7 | Group={{ GROUP }} 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /frontend/public/locales/en/404.json: -------------------------------------------------------------------------------- 1 | { 2 | "404-information": "We're sorry, the page you requested could not be found. Please go back to the homepage", 3 | "user-not-found": "User not registered with Github.", 4 | "check-username": "Please check the user's name and search again." 5 | } 6 | -------------------------------------------------------------------------------- /infra/renew.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run -it --rm --name certbot \ 4 | -v '/etc/letsencrypt:/etc/letsencrypt' \ 5 | -v '/var/lib/letsencrypt:/var/lib/letsencrypt' \ 6 | certbot/certbot renew --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory 7 | -------------------------------------------------------------------------------- /frontend/public/locales/en/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "search-placeholder": "Search for the user", 3 | "overall-ranking": "Overall Ranking", 4 | "rising-user-top-3": "TOP3 Rising User", 5 | "daily-views-top-3": "TOP3 Daily Views", 6 | "most-programming-lang-top-3": "TOP3 Programming Language" 7 | } 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: New feature request 4 | title: "[Feature] " 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | # 📘 설명 11 | 12 | # 📗 세부사항 13 | 14 | # 📙 체크리스트 15 | 16 | - [ ] 체크리스트1 17 | - [ ] 체크리스트2 18 | - [ ] 체크리스트3 19 | -------------------------------------------------------------------------------- /backend/libs/common/decorators/current-user.decodator.ts: -------------------------------------------------------------------------------- 1 | import { ExecutionContext, createParamDecorator } from '@nestjs/common'; 2 | 3 | export const CurrentUser = createParamDecorator((data: unknown, ctx: ExecutionContext) => { 4 | const request = ctx.switchToHttp().getRequest(); 5 | return request.user; 6 | }); 7 | -------------------------------------------------------------------------------- /frontend/public/locales/ko/ranking.json: -------------------------------------------------------------------------------- 1 | { 2 | "search-placeholder": "사용자명", 3 | "search-not-found": "사용자가 존재하지 않습니다.", 4 | "search-user": "\"{{username}}\"에 대한 검색 결과 입니다.", 5 | "rank_one": "{{count}}등", 6 | "rank_two": "{{count}}등", 7 | "rank_few": "{{count}}등", 8 | "rank_other": "{{count}}등" 9 | } 10 | -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-alpine3.14 2 | 3 | COPY nest-cli.json . 4 | COPY tsconfig.build.json . 5 | COPY tsconfig.json . 6 | COPY package.json . 7 | COPY yarn.lock . 8 | 9 | RUN yarn install 10 | 11 | COPY libs ./libs 12 | COPY src ./src 13 | 14 | RUN yarn build 15 | 16 | ENTRYPOINT ["yarn", "start:prod"] 17 | -------------------------------------------------------------------------------- /frontend/public/locales/en/ranking.json: -------------------------------------------------------------------------------- 1 | { 2 | "search-placeholder": "Username", 3 | "search-not-found": "User does not exist", 4 | "search-user": "Search results for \"{{username}}\"", 5 | "rank_one": "{{count}}st", 6 | "rank_two": "{{count}}nd", 7 | "rank_few": "{{count}}rd", 8 | "rank_other": "{{count}}th" 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import useAutoComplete from './useAutoComplete'; 2 | import useDebounce from './useDebounce'; 3 | import useDropdown from './useDropdown'; 4 | import useInput from './useInput'; 5 | import useQueryData from './useQueryData'; 6 | 7 | export { useAutoComplete, useInput, useDebounce, useDropdown, useQueryData }; 8 | -------------------------------------------------------------------------------- /.github/workflows/auto-author-assign.yml: -------------------------------------------------------------------------------- 1 | name: 'Auto Author Assign' 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, reopened] 6 | 7 | permissions: 8 | pull-requests: write 9 | 10 | jobs: 11 | assign-author: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: toshimaru/auto-author-assign@v1.6.1 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/auto-label-in-issue.yml: -------------------------------------------------------------------------------- 1 | name: 'Auto Label' 2 | 3 | on: 4 | pull_request: 5 | types: [labeled, unlabeled, opened, synchronize, reopened] 6 | 7 | permissions: 8 | pull-requests: write 9 | 10 | jobs: 11 | auto-label: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: Yaminyam/auto-label-in-issue@1.1.0 15 | -------------------------------------------------------------------------------- /backend/src/auth/types/github-profile.interface.ts: -------------------------------------------------------------------------------- 1 | export interface GithubProfile { 2 | node_id: string; 3 | login: string; 4 | following: number; 5 | followers: number; 6 | avatar_url?: string; 7 | name?: string; 8 | company?: string; 9 | blog?: string; 10 | location?: string; 11 | email?: string; 12 | bio?: string; 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | getHello(): string { 10 | return this.appService.healthCheck(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/contexts/dropdown.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface DropdownContextType { 4 | ref: React.RefObject; 5 | isOpen: boolean; 6 | toggleDropdown: () => void; 7 | closeDropdown: (e: MouseEvent) => void; 8 | } 9 | 10 | export const DropdownContext = React.createContext({} as DropdownContextType); 11 | -------------------------------------------------------------------------------- /frontend/public/locales/ko/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "login-button": "로그인", 3 | "my-profile": "내 프로필", 4 | "logout": "로그아웃", 5 | "current-locale": "한국어", 6 | "table-tier": "티어", 7 | "table-user": "사용자", 8 | "table-score": "점수", 9 | "table-programming-lang": "언어", 10 | "table-user-num": "사용자수", 11 | "table-views": "조회수", 12 | "table-tech-stack": "기술스택" 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/batch/batch.module.ts: -------------------------------------------------------------------------------- 1 | import { UserModule } from '@apps/user/user.module'; 2 | import { Module } from '@nestjs/common'; 3 | import { ScheduleModule } from '@nestjs/schedule'; 4 | import { BatchService } from './batch.service'; 5 | 6 | @Module({ 7 | imports: [ScheduleModule.forRoot(), UserModule], 8 | providers: [BatchService], 9 | }) 10 | export class BatchModule {} 11 | -------------------------------------------------------------------------------- /frontend/.storybook/i18n.js: -------------------------------------------------------------------------------- 1 | import i18n from 'i18next'; 2 | import LanguageDetector from 'i18next-browser-languagedetector'; 3 | import Backend from 'i18next-http-backend'; 4 | import { initReactI18next } from 'react-i18next'; 5 | 6 | i18n.use(Backend).use(LanguageDetector).use(initReactI18next).init({ 7 | fallbackLng: 'ko', 8 | debug: true, 9 | }); 10 | 11 | export default i18n; 12 | -------------------------------------------------------------------------------- /backend/src/user/dto/user.profile.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { UserDto } from './user.dto'; 3 | 4 | export class UserProfileDto extends UserDto { 5 | @ApiProperty() 6 | totalRank: number; 7 | 8 | @ApiProperty() 9 | tierRank: number; 10 | 11 | @ApiProperty() 12 | startExp: number; 13 | 14 | @ApiProperty() 15 | needExp: number; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/hooks/useDropdown.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { DropdownContext } from '@contexts/dropdown'; 3 | 4 | function useDropdown() { 5 | const context = React.useContext(DropdownContext); 6 | if (context === undefined) { 7 | throw new Error('useToggle must be used within a '); 8 | } 9 | return context; 10 | } 11 | 12 | export default useDropdown; 13 | -------------------------------------------------------------------------------- /frontend/src/components/Profile/index.ts: -------------------------------------------------------------------------------- 1 | import CommitHistory from './CommitHistory'; 2 | import EXPbar from './EXPbar'; 3 | import PinnedRepository from './PinnedRepository'; 4 | import ProfileCard from './ProfileCard'; 5 | import ProfileLabel from './ProfileLabel'; 6 | import Statistic from './Statistic'; 7 | 8 | export { CommitHistory, Statistic, ProfileCard, ProfileLabel, PinnedRepository, EXPbar }; 9 | -------------------------------------------------------------------------------- /backend/src/ranking/dto/ranking-language.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class RankingLanguageDto { 4 | @ApiProperty() 5 | language: string; 6 | 7 | @ApiProperty() 8 | count: number; 9 | 10 | of(language: string, count: number): RankingLanguageDto { 11 | this.language = language; 12 | this.count = count; 13 | return this; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/public/icons/arrow-bar.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/public/locales/en/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "login-button": "LOGIN", 3 | "my-profile": "Profile", 4 | "logout": "Logout", 5 | "current-locale": "English", 6 | "table-tier": "Tier", 7 | "table-user": "User", 8 | "table-score": "Score", 9 | "table-programming-lang": "Lang", 10 | "table-user-num": "Number of users", 11 | "table-views": "Views", 12 | "table-tech-stack": "Tech Stack" 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/components/Footer/Footer.stories.tsx: -------------------------------------------------------------------------------- 1 | import { ComponentMeta, ComponentStory } from '@storybook/react'; 2 | import Footer from '@components/Footer'; 3 | 4 | export default { 5 | title: 'Components/Footer', 6 | component: Footer, 7 | } as ComponentMeta; 8 | 9 | const Template: ComponentStory = () =>