diff --git a/src/main/java/myblog/blog/BlogApplication.java b/src/main/java/myblog/blog/BlogApplication.java index d5d40b0..e86eca6 100644 --- a/src/main/java/myblog/blog/BlogApplication.java +++ b/src/main/java/myblog/blog/BlogApplication.java @@ -2,8 +2,10 @@ package myblog.blog; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication +@EnableJpaAuditing public class BlogApplication { public static void main(String[] args) { diff --git a/src/main/java/myblog/blog/article/controller/ArticleController.java b/src/main/java/myblog/blog/article/controller/ArticleController.java new file mode 100644 index 0000000..898660f --- /dev/null +++ b/src/main/java/myblog/blog/article/controller/ArticleController.java @@ -0,0 +1,9 @@ +package myblog.blog.article.controller; + +import org.springframework.stereotype.Controller; + +@Controller +public class ArticleController { + + +} diff --git a/src/main/java/myblog/blog/article/domain/Article.java b/src/main/java/myblog/blog/article/domain/Article.java new file mode 100644 index 0000000..4442226 --- /dev/null +++ b/src/main/java/myblog/blog/article/domain/Article.java @@ -0,0 +1,33 @@ +package myblog.blog.article.domain; + +import lombok.Getter; +import myblog.blog.member.doamin.Member; + +import javax.persistence.*; + +@Entity +@Getter +@SequenceGenerator( + name = "article_seq_generator", + sequenceName = "article_seq", + initialValue = 1, allocationSize = 50) +public class Article { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "article_generator") + @Column(name = "article_id") + private Long id; + + @Column(nullable = false) + private String title; + @Column(nullable = false) + private String content; + private Long hit; + private String toc; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + + +} diff --git a/src/main/java/myblog/blog/article/repository/ArticleRepository.java b/src/main/java/myblog/blog/article/repository/ArticleRepository.java new file mode 100644 index 0000000..e335067 --- /dev/null +++ b/src/main/java/myblog/blog/article/repository/ArticleRepository.java @@ -0,0 +1,11 @@ +package myblog.blog.article.repository; + +import myblog.blog.article.domain.Article; +import org.springframework.data.jpa.repository.JpaRepository; + + +public interface ArticleRepository extends JpaRepository { + + + +} diff --git a/src/main/java/myblog/blog/article/service/ArticleService.java b/src/main/java/myblog/blog/article/service/ArticleService.java new file mode 100644 index 0000000..5370388 --- /dev/null +++ b/src/main/java/myblog/blog/article/service/ArticleService.java @@ -0,0 +1,29 @@ +package myblog.blog.article.service; + +import lombok.RequiredArgsConstructor; +import myblog.blog.article.domain.Article; +import myblog.blog.article.repository.ArticleRepository; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ArticleService { + + private final ArticleRepository articleRepository; + private final ModelMapper modelMapper; + + public Long writeArticle(NewArticleDto newArticleDto){ + + Article article = modelMapper.map(newArticleDto, Article.class); + + articleRepository.save(article); + + return article.getId(); + + } + + + + +} diff --git a/src/main/java/myblog/blog/article/service/NewArticleDto.java b/src/main/java/myblog/blog/article/service/NewArticleDto.java new file mode 100644 index 0000000..4b717ea --- /dev/null +++ b/src/main/java/myblog/blog/article/service/NewArticleDto.java @@ -0,0 +1,18 @@ +package myblog.blog.article.service; + +import myblog.blog.member.doamin.Member; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; + +public class NewArticleDto { + + @NotBlank + private String title; + @NotBlank + private String content; + private String toc; + @NotBlank + private Long memberId; + +} diff --git a/src/main/java/myblog/blog/config/SecurityConfig.java b/src/main/java/myblog/blog/base/config/SecurityConfig.java similarity index 54% rename from src/main/java/myblog/blog/config/SecurityConfig.java rename to src/main/java/myblog/blog/base/config/SecurityConfig.java index 8198f1f..a376ec5 100644 --- a/src/main/java/myblog/blog/config/SecurityConfig.java +++ b/src/main/java/myblog/blog/base/config/SecurityConfig.java @@ -1,9 +1,13 @@ -package myblog.blog.config; +package myblog.blog.base.config; import lombok.RequiredArgsConstructor; +import myblog.blog.exception.LoginFailHandler; +import myblog.blog.member.doamin.Role; import myblog.blog.member.service.Oauth2MemberService; +import org.springframework.boot.autoconfigure.security.servlet.PathRequest; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -13,32 +17,40 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur public class SecurityConfig extends WebSecurityConfigurerAdapter { private final Oauth2MemberService oauth2MemberService; + private final LoginFailHandler loginFailHandler; + @Override + public void configure(WebSecurity web) throws Exception { + web + .ignoring() + .antMatchers("/css/**", "/node_modules/**") + .requestMatchers(PathRequest.toStaticResources().atCommonLocations()); + } @Override protected void configure(HttpSecurity http) throws Exception { http - .csrf().disable() - .authorizeRequests() - .antMatchers("/user").authenticated() - .antMatchers("/admin").access("hasRole('ROLE_ADMIN')") - .anyRequest() - .permitAll() + .antMatchers("/admin").hasRole(Role.ADMIN.name()) + .anyRequest().permitAll() .and() .formLogin() + .loginPage("/login") .and() .logout() .logoutSuccessUrl("/") + .deleteCookies("JSESSIONID","remember-me") + .and() .oauth2Login() + .loginPage("/login") + .failureHandler(loginFailHandler) .userInfoEndpoint() .userService(oauth2MemberService) - ; } } diff --git a/src/main/java/myblog/blog/base/domain/BasicEntity.java b/src/main/java/myblog/blog/base/domain/BasicEntity.java new file mode 100644 index 0000000..818f847 --- /dev/null +++ b/src/main/java/myblog/blog/base/domain/BasicEntity.java @@ -0,0 +1,24 @@ +package myblog.blog.base.domain; + +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import java.time.LocalDateTime; + +@EntityListeners(AuditingEntityListener.class) +@MappedSuperclass +@Getter +public abstract class BasicEntity { + + @CreatedDate + @Column(updatable = false) + private LocalDateTime createdDate; + @LastModifiedDate + private LocalDateTime updatedDate; + +} diff --git a/src/main/java/myblog/blog/exception/DuplicateEmailException.java b/src/main/java/myblog/blog/exception/DuplicateEmailException.java deleted file mode 100644 index 3d1dd02..0000000 --- a/src/main/java/myblog/blog/exception/DuplicateEmailException.java +++ /dev/null @@ -1,24 +0,0 @@ -package myblog.blog.exception; - -public class DuplicateEmailException extends RuntimeException { - - public DuplicateEmailException() { - super("이미 가입한 이메일입니다."); - } - - public DuplicateEmailException(String message) { - super(message); - } - - public DuplicateEmailException(String message, Throwable cause) { - super(message, cause); - } - - public DuplicateEmailException(Throwable cause) { - super(cause); - } - - protected DuplicateEmailException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} diff --git a/src/main/java/myblog/blog/exception/ExceptionController.java b/src/main/java/myblog/blog/exception/ExceptionController.java new file mode 100644 index 0000000..5e4ac64 --- /dev/null +++ b/src/main/java/myblog/blog/exception/ExceptionController.java @@ -0,0 +1,23 @@ +package myblog.blog.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ControllerAdvice; + +@ControllerAdvice +@Slf4j +public class ExceptionController { + +// @ExceptionHandler +// public String handleRuntimeException(Principal principal, HttpServletRequest req, RuntimeException e) { +// if (principal != null) { +// log.info("'{}' requested '{}' ", principal.getName(), req.getRequestURI()); +// } else { +// log.info("requested '{}'", req.getRequestURI()); +// } +// +// log.error("bad request", e); +// return ""; +// } + + +} diff --git a/src/main/java/myblog/blog/exception/LoginFailHandler.java b/src/main/java/myblog/blog/exception/LoginFailHandler.java new file mode 100644 index 0000000..510231d --- /dev/null +++ b/src/main/java/myblog/blog/exception/LoginFailHandler.java @@ -0,0 +1,34 @@ +package myblog.blog.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +@Slf4j +public class LoginFailHandler extends SimpleUrlAuthenticationFailureHandler { + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + + String errMsg = "error"; + + if(exception instanceof OAuth2AuthenticationException){ + errMsg = "duplicatedEmail"; + request.setAttribute("errMsg", errMsg); + + } + + setDefaultFailureUrl("/login?error="+errMsg); + super.onAuthenticationFailure(request, response, exception); + } +} diff --git a/src/main/java/myblog/blog/MainController.java b/src/main/java/myblog/blog/main/MainController.java similarity index 59% rename from src/main/java/myblog/blog/MainController.java rename to src/main/java/myblog/blog/main/MainController.java index a8bc15c..4b3a13f 100644 --- a/src/main/java/myblog/blog/MainController.java +++ b/src/main/java/myblog/blog/main/MainController.java @@ -1,21 +1,17 @@ -package myblog.blog; +package myblog.blog.main; -import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequiredArgsConstructor public class MainController { - @GetMapping("/") public String main() { - return "index"; + return "main"; } diff --git a/src/main/java/myblog/blog/member/auth/PrincipalDetails.java b/src/main/java/myblog/blog/member/auth/PrincipalDetails.java index b27f70d..81eda5d 100644 --- a/src/main/java/myblog/blog/member/auth/PrincipalDetails.java +++ b/src/main/java/myblog/blog/member/auth/PrincipalDetails.java @@ -2,9 +2,13 @@ package myblog.blog.member.auth; import myblog.blog.member.doamin.Member; +import myblog.blog.member.doamin.Role; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.core.user.OAuth2User; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Map; @@ -27,7 +31,9 @@ public class PrincipalDetails implements OAuth2User { @Override public Collection getAuthorities() { - return null; + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(member.getRole().getValue())); + return authorities; } @Override diff --git a/src/main/java/myblog/blog/member/auth/UserInfoFactory.java b/src/main/java/myblog/blog/member/auth/UserInfoFactory.java index 6eef9cb..2dc4d33 100644 --- a/src/main/java/myblog/blog/member/auth/UserInfoFactory.java +++ b/src/main/java/myblog/blog/member/auth/UserInfoFactory.java @@ -1,6 +1,7 @@ package myblog.blog.member.auth; import myblog.blog.member.auth.userinfo.*; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Component; @@ -19,7 +20,8 @@ public class UserInfoFactory { } else if (oAuth2UserRequest.getClientRegistration().getRegistrationId().equals("naver")) { return new NaverUserInfo(oAuth2User.getAttribute("response")); } - else return null; + else { + throw new IllegalArgumentException("지원하지 않는 Oauth 인증 시도입니다");} } diff --git a/src/main/java/myblog/blog/member/controller/MemberController.java b/src/main/java/myblog/blog/member/controller/MemberController.java new file mode 100644 index 0000000..79eb4eb --- /dev/null +++ b/src/main/java/myblog/blog/member/controller/MemberController.java @@ -0,0 +1,23 @@ +package myblog.blog.member.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +public class MemberController { + + @GetMapping("/login") + public String loginFrom(@RequestParam(value = "error",required = false) String error, Model model){ + + if(error!=null&&error.equals("duplicatedEmail")){ + model.addAttribute("errMsg","이미 가입된 이메일입니다."); + } + + return "login"; + + } + + +} diff --git a/src/main/java/myblog/blog/member/doamin/Member.java b/src/main/java/myblog/blog/member/doamin/Member.java index cd17056..58310dc 100644 --- a/src/main/java/myblog/blog/member/doamin/Member.java +++ b/src/main/java/myblog/blog/member/doamin/Member.java @@ -2,8 +2,11 @@ package myblog.blog.member.doamin; import lombok.Builder; import lombok.Getter; +import myblog.blog.article.domain.Article; +import myblog.blog.base.domain.BasicEntity; import javax.persistence.*; +import java.util.List; @Entity @SequenceGenerator( @@ -11,10 +14,11 @@ import javax.persistence.*; sequenceName = "MEMBER_SEQ", initialValue = 1, allocationSize = 50) @Getter -public class Member { +public class Member extends BasicEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR") + @Column(name = "member_id") private Long id; @Column(nullable = false) @@ -23,7 +27,7 @@ public class Member { @Column(nullable = false) private String userId; - @Column(nullable = false) + @Column(nullable = false, unique = true) private String email; private String picUrl; @@ -35,6 +39,9 @@ public class Member { private String providerId; + @OneToMany(mappedBy = "member") + private List
articles; + protected Member() { } diff --git a/src/main/java/myblog/blog/member/doamin/Role.java b/src/main/java/myblog/blog/member/doamin/Role.java index dad9467..c7f8c65 100644 --- a/src/main/java/myblog/blog/member/doamin/Role.java +++ b/src/main/java/myblog/blog/member/doamin/Role.java @@ -3,12 +3,12 @@ package myblog.blog.member.doamin; import lombok.Getter; import lombok.RequiredArgsConstructor; -@Getter @RequiredArgsConstructor +@Getter public enum Role { - USER("ROLE_USER","일반사용자"), - ADMIN("ROLE_ADMIN","관리자"); + USER("ROLE_USER"), + ADMIN("ROLE_ADMIN"); + + private final String value; - private final String key; - private final String title; } diff --git a/src/main/java/myblog/blog/member/service/Oauth2MemberService.java b/src/main/java/myblog/blog/member/service/Oauth2MemberService.java index 05c061d..4d84996 100644 --- a/src/main/java/myblog/blog/member/service/Oauth2MemberService.java +++ b/src/main/java/myblog/blog/member/service/Oauth2MemberService.java @@ -1,7 +1,6 @@ package myblog.blog.member.service; import lombok.RequiredArgsConstructor; -import myblog.blog.exception.DuplicateEmailException; import myblog.blog.member.auth.userinfo.Oauth2UserInfo; import myblog.blog.member.auth.UserInfoFactory; import myblog.blog.member.repository.MemberRepository; @@ -25,6 +24,7 @@ public class Oauth2MemberService extends DefaultOAuth2UserService { private final MemberRepository memberRepository; private final UserInfoFactory userInfoFactory; + // 앱 구동시 ADMIN 계정 Insert @Value("${admin.username}") private String adminUsername; @Value("${admin.picUrl}") @@ -37,6 +37,7 @@ public class Oauth2MemberService extends DefaultOAuth2UserService { private String adminProvider; @Override + @Transactional public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { Oauth2UserInfo userInfo = @@ -54,7 +55,7 @@ public class Oauth2MemberService extends DefaultOAuth2UserService { if(member == null) { if(memberRepository.findByEmail(userInfo.getEmail()) != null) - throw new DuplicateEmailException(); + throw new OAuth2AuthenticationException("duplicateEmail"); member = Member.builder() .username(userInfo.getUserName()) diff --git a/src/main/resources/static/css/login.css b/src/main/resources/static/css/login.css new file mode 100644 index 0000000..fad7820 --- /dev/null +++ b/src/main/resources/static/css/login.css @@ -0,0 +1,58 @@ + +button img{ + width: 200px; + height:50px; +} + +/* Social Buttons Style */ +.btnCtm { + background: none; + border: none; + background-position: 1em; + background-repeat: no-repeat; + background-size: 2em; + border-radius: 0.5em; + border: none; + color: white; + cursor: pointer; + font-size: 0.9em; + line-height: 1em; + padding: 0 2em 0 4em; + text-decoration: none; + transition: all 0.5s; + height: 50px; + width: 200px; + margin-bottom: 5px; +} + +.btn-google { + background-color: #dd4b39; + background-image: url("/img/google.svg"); +} +.btn-google:hover { + background-color: #e47365; +} +.btn-google:active { + background-color: #c23321; +} +.btn-facebook { + background-color: #3b5998; + background-image: url("/img/facebook.svg"); +} +.btn-facebook:hover { + background-color: #4c70ba; +} +.btn-facebook:active { + background-color: #2d4373; +} + +.btn-kakao{ + background: none; + border: none; +} + +.btn-naver{ + background: none; + border: none; + +} diff --git a/src/main/resources/static/css/mainCss.css b/src/main/resources/static/css/mainCss.css new file mode 100644 index 0000000..3998ec6 --- /dev/null +++ b/src/main/resources/static/css/mainCss.css @@ -0,0 +1,333 @@ +@font-face { + font-family: "IM_Hyemin-Bold"; + src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2106@1.1/IM_Hyemin-Bold.woff2") + format("woff"); + font-weight: normal; + font-style: normal; +} + +a { + text-decoration: none; + color: rgb(65, 65, 65); +} +a:visited { + text-decoration: none; + color: rgb(65, 65, 65); +} +a:active { + text-decoration: none; + color: rgb(65, 65, 65); +} +a:hover { + text-decoration: none; + color: rgb(65, 65, 65); +} + +body { + background-color: #fcf6f5; + font-family: "IM_Hyemin-Bold"; +} + +body::-webkit-scrollbar { + width: 15px; +} + +body::-webkit-scrollbar-thumb { + background-color: rgb(233, 224, 224); + background-clip: padding-box; + border: 4px solid transparent; + border-top-left-radius: 100px; + border-bottom-right-radius: 100px; +} + +body::-webkit-scrollbar-track { + background-color: #f1f1f1; +} + +.offcanvas-body::-webkit-scrollbar { + width: 15px; +} + +.offcanvas-body::-webkit-scrollbar-thumb { + background-color: rgb(233, 224, 224); + background-clip: padding-box; + border: 4px solid transparent; + border-top-left-radius: 100px; + border-bottom-right-radius: 100px; +} + +.offcanvas-body::-webkit-scrollbar-track { + background-color: #fcf6f5; +} + +.btn-toggle { + display: inline-flex; + align-items: center; + padding: 0.25rem 0.5rem; + font-weight: 600; + color: rgba(0, 0, 0, 0.65); + background-color: transparent; + border: 0; +} +.btn-toggle:hover, +.btn-toggle:focus { + color: rgba(0, 0, 0, 0.85); + background-color: #d2f4ea; +} + +.btn-toggle::before { + width: 1.25em; + line-height: 0; + content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e"); + transition: transform 0.35s ease; + transform-origin: 0.5em 50%; +} + +.btn-toggle[aria-expanded="true"] { + color: rgba(0, 0, 0, 0.85); +} +.btn-toggle[aria-expanded="true"]::before { + transform: rotate(90deg); +} + +.btn-toggle-nav a { + display: inline-flex; + padding: 0.1875rem 0.5rem; + margin-top: 0.125rem; + margin-left: 1.25rem; + text-decoration: none; +} +.btn-toggle-nav a:hover, +.btn-toggle-nav a:focus { + background-color: #d2f4ea; +} + +.navbar { + opacity: 0.98; +} +.navbar-toggler { + border: whitesmoke; +} +.fa-address-card { + font-size: 24px; + color: rgb(119, 119, 119); +} + +.cover { + object-fit: cover; +} + +.carousel-item { + cursor: pointer; +} + +.cards-container { + max-width: 1200px; +} + +.popular-card { + overflow: hidden; + cursor: pointer; +} + +.card-description { + position: absolute; + top: calc(100% - 50px); + height: 50px; + overflow: hidden; + + background: rgb(56, 55, 55); + color: aliceblue; + width: 100%; + opacity: 0.8; + + transition-property: all; + transition-duration: 0.7s; +} + +.card-des-overlay { + position: absolute; + overflow: hidden; + background: rgb(56, 55, 55); + color: aliceblue; + width: 100%; + height: 100%; + top: 100%; + opacity: 0.8; + + transition-property: all; + transition-duration: 0.7s; +} + +.popular-card:hover .card-des-overlay { + top: 0%; +} +.popular-card:hover .card-description { + top: 100%; +} + +.recent-card-text { + overflow: hidden; + position: relative; + line-height: 1.2em; + max-height: 3.6em; + margin-right: -1em; +} + +.recent-card-text:before { + content: "..."; + position: absolute; + right: 4px; + bottom: 0; +} + +.recent-card-text:after { + content: ""; + position: absolute; + right: 4px; + width: 1em; + height: 1em; + margin-top: 0.2em; + background: white; +} + +.recent-card { + transition-property: all; + transition-duration: 1s; +} + +.recent-card:hover { + box-shadow: 0px 4px 8px rgba(38, 38, 38, 0.2); + top: -4px; + border: 1px solid #cccccc; + background-color: white; + cursor: pointer; +} + +.arrow-up { + position: fixed; + bottom: 50px; + right: 50px; + font-size: 50px; + border-radius: 100%; + border: white; + width: 70px; + height: 70px; + color: white; + background-color: rgb(241, 226, 89); + z-index: 5; + opacity: 0.7; +} + +.sidebar { + position: fixed; + height: 100vh; + z-index: 1000; +} + +.sidebar::-webkit-scrollbar { + width: 15px; +} + +.sidebar::-webkit-scrollbar-thumb { + background-color: rgb(233, 224, 224, 0.7); + background-clip: padding-box; + border: 4px solid transparent; + border-top-left-radius: 100px; + border-bottom-right-radius: 100px; +} + +.sidebar::-webkit-scrollbar-track { + background-color: #f1f1f1; +} + +@media (max-width: 767.98px) { + small { + font-size: 10px; + } + + .arrow-up { + width: 50px; + height: 50px; + font-size: 30px; + line-height: 50px; + right: 16px; + bottom: 16px; + } +} + +/* list */ +.pagination-outer { + text-align: center; +} +.pagination { + display: inline-flex; +} +.pagination li { + margin: 0 5px; +} +.pagination li a.page-link { + color: #999; + background: #e7e7e7; + font-size: 16px; + font-weight: 700; + line-height: 30px; + height: 30px; + width: 30px; + padding: 0; + border: none; + border-radius: 5px; + display: block; + position: relative; +} +.pagination li.active a.page-link, +.pagination li a.page-link:hover, +.pagination li.active a.page-link:hover, +.pagination li a.page-link:focus { + color: rgb(12, 11, 11); + background: linear-gradient(#f0f5b0, #f8fae3); + border: none; +} + +@media only screen and (max-width: 480px) { + .pagination { + font-size: 0; + display: block; + } + .pagination li { + margin: 5px 0; + display: inline-block; + } +} + +.form-color { + background-color: #e0dedd; +} + +.form-control { + border-radius: 25px; + height: fit-content; + resize: none; +} + +.form-control:focus { + color: #495057; + background-color: #fcf6f5; + border-color: #35b69f; + outline: 0; + box-shadow: none; + text-indent: 10px; +} + +.comment-text { + font-size: 13px; +} + +.comment-reply { + background-color: #e0dedd; +} + +.user-feed { + font-size: 14px; + margin-top: 12px; +} diff --git a/src/main/resources/static/img/facebook.svg b/src/main/resources/static/img/facebook.svg new file mode 100644 index 0000000..0cd9ea9 --- /dev/null +++ b/src/main/resources/static/img/facebook.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/main/resources/static/img/google.svg b/src/main/resources/static/img/google.svg new file mode 100644 index 0000000..61b41af --- /dev/null +++ b/src/main/resources/static/img/google.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/src/main/resources/static/img/kakao.png b/src/main/resources/static/img/kakao.png new file mode 100644 index 0000000..09bb358 Binary files /dev/null and b/src/main/resources/static/img/kakao.png differ diff --git a/src/main/resources/static/img/naver.png b/src/main/resources/static/img/naver.png new file mode 100644 index 0000000..1e7bfe6 Binary files /dev/null and b/src/main/resources/static/img/naver.png differ diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html deleted file mode 100644 index 3129e86..0000000 --- a/src/main/resources/templates/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Title - - -ㅎㅇ - -
-
- 아이디
-
- - -구글 로그인 -페이스북 로그인 -네이버 로그인 -카카오 로그인 -로그아웃 - - - \ No newline at end of file diff --git a/src/main/resources/templates/layout/layout.html b/src/main/resources/templates/layout/layout.html new file mode 100644 index 0000000..328a576 --- /dev/null +++ b/src/main/resources/templates/layout/layout.html @@ -0,0 +1,294 @@ + + + + + + + Jinia's Log + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + +
+ + + + + + + +
+
+ + + +
+ +
+ + +
+
+
Copyright ©Jinia
+
+
+
+
+ + + + + + + + + + + diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..917727c --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,54 @@ + + + + + Jinia's Log - 로그인 + + + + +
+ +
+ +
+
+ +

로그인 오류

+ + + + + +
+ + + +
+ +
+ + + +
+
+
+ +
+ + +
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/main.html b/src/main/resources/templates/main.html new file mode 100644 index 0000000..b500cea --- /dev/null +++ b/src/main/resources/templates/main.html @@ -0,0 +1,332 @@ + + + + + Jinia's Log + + + + +
+ + + + + +
+ +
+ +
+

최신 포스팅

+
+ + + + + + + + + + + +
+
+
+ + + \ No newline at end of file diff --git a/src/test/java/myblog/blog/MainControllerTest.java b/src/test/java/myblog/blog/MainControllerTest.java new file mode 100644 index 0000000..03373b4 --- /dev/null +++ b/src/test/java/myblog/blog/MainControllerTest.java @@ -0,0 +1,33 @@ +package myblog.blog; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +class MainControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Test + @WithMockUser(roles = "ADMIN") + public void 권한테스트() throws Exception { + // given + // when + ResultActions admin = mockMvc.perform(get("/admin")); + // then + admin.andExpect(status().isOk()); + + } +} \ No newline at end of file diff --git a/src/test/java/myblog/blog/member/service/Oauth2MemberServiceTest.java b/src/test/java/myblog/blog/member/service/Oauth2MemberServiceTest.java new file mode 100644 index 0000000..8cdd873 --- /dev/null +++ b/src/test/java/myblog/blog/member/service/Oauth2MemberServiceTest.java @@ -0,0 +1,37 @@ +package myblog.blog.member.service; + +import myblog.blog.member.doamin.Member; +import myblog.blog.member.doamin.Role; +import myblog.blog.member.repository.MemberRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@SpringBootTest +class Oauth2MemberServiceTest { + + @Autowired + Oauth2MemberService oauth2MemberService; + + @Autowired + MemberRepository memberRepository; + + @Test + public void 권한테스트() throws Exception { + // given + Optional byId = memberRepository.findById(1L); + Member admin = byId.get(); + + + // when + + // then + assertThat(admin.getRole().toString()).isEqualTo(Role.ADMIN.toString()); + + } + +} \ No newline at end of file