diff --git a/.drone.yml b/.drone.yml index ddbc078..7b5b258 100644 --- a/.drone.yml +++ b/.drone.yml @@ -51,10 +51,11 @@ type: docker name: frontend steps: - name: prettier - image: elnebuloso/prettier + image: node:alpine commands: - cd docker/frontend/ - - prettier . -c + - yarn global add prettier eslint + - yarn lint - name: build image: tmaier/docker-compose volumes: diff --git a/docker-compose.yml b/docker-compose.yml index 256429b..8bc5be0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,6 +54,9 @@ services: - default - db - redis + depends_on: + - db + - redis environment: URL: ${URL} BACKEND_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_USER} @@ -65,9 +68,6 @@ services: BACKEND_SMTP_NAME: ${BACKEND_SMTP_NAME} BACKEND_SMTP_USERNAME: ${BACKEND_SMTP_USERNAME} BACKEND_SMTP_PASSWORD: ${BACKEND_SMTP_PASSWORD} - depends_on: - - db - - redis frontend: build: @@ -80,6 +80,8 @@ services: - default depends_on: - backend + environment: + NODE_ENV: production networks: db: diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index 26ea8eb..83f53c6 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -1,10 +1,10 @@ -FROM crystallang/crystal:latest-alpine as deps +FROM crystallang/crystal:1.3-alpine as deps WORKDIR /app RUN apk add curl --no-cache COPY ./shard.yml ./shard.lock ./ RUN shards install --production -FROM crystallang/crystal:latest-alpine as builder +FROM crystallang/crystal:1.3-alpine as builder ARG BUILD_ENV WORKDIR /app COPY --from=deps /app/shard.yml /app/shard.lock ./ diff --git a/docker/backend/src/backend/api/auth.cr b/docker/backend/src/backend/api/auth.cr index 45f8266..7b35690 100644 --- a/docker/backend/src/backend/api/auth.cr +++ b/docker/backend/src/backend/api/auth.cr @@ -22,7 +22,7 @@ module Backend JWT.encode({"data" => data.to_h, "exp" => expiration}, SAFE_ENV["BACKEND_JWT_SECRET"], JWT::Algorithm::HS256) end - def create_user_jwt(user_id : Int, expiration : Int = (Time.utc + Time::Span.new(hours: 6)).to_unix) : String + def create_user_jwt(user_id : Int, expiration : Int = (Time.utc + Time::Span.new(days: 1)).to_unix) : String create_jwt({user_id: user_id}, expiration) end diff --git a/docker/backend/src/backend/api/schema/mutation.cr b/docker/backend/src/backend/api/schema/mutation.cr index dc35a86..cb0c70e 100644 --- a/docker/backend/src/backend/api/schema/mutation.cr +++ b/docker/backend/src/backend/api/schema/mutation.cr @@ -1,12 +1,16 @@ +require "CrystalEmail" + module Backend module API module Schema @[GraphQL::Object] class Mutation < GraphQL::BaseMutation @[GraphQL::Field] - def login(input : LoginInput) : LoginPayload - user = Db::User.find_by(email: input.email) - raise "Auth failed" unless user && Auth.verify_password?(input.password, user.password) + def login(email : String, password : String) : LoginPayload + raise "Auth failed" if email.empty? || password.empty? || !CrystalEmail::Rfc5322::Public.validates?(email) + + user = Db::User.find_by(email: email) + raise "Auth failed" unless user && Auth.verify_password?(password, user.password) LoginPayload.new( user: User.new(user), diff --git a/docker/backend/src/backend/api/schema/user.cr b/docker/backend/src/backend/api/schema/user.cr index eb631d6..51fe036 100644 --- a/docker/backend/src/backend/api/schema/user.cr +++ b/docker/backend/src/backend/api/schema/user.cr @@ -102,18 +102,18 @@ module Backend end end - @[GraphQL::InputObject] - class LoginInput < GraphQL::BaseInputObject - getter email - getter password + # @[GraphQL::InputObject] + # class LoginInput < GraphQL::BaseInputObject + # getter email + # getter password - @[GraphQL::Field] - def initialize( - @email : String, - @password : String - ) - end - end + # @[GraphQL::Field] + # def initialize( + # @email : String, + # @password : String + # ) + # end + # end @[GraphQL::Object] class LoginPayload < GraphQL::BaseObject diff --git a/docker/backend/src/backend/run.cr b/docker/backend/src/backend/run.cr index 3d77ffd..4865f43 100644 --- a/docker/backend/src/backend/run.cr +++ b/docker/backend/src/backend/run.cr @@ -2,7 +2,10 @@ module Backend extend self def run : Nil - Log.info { "Starting backend services..." } + {% if flag?(:development) %} + Log.warn { "Backend is running in development mode! Do not use this in production!" } + {% end %} + Log.info { "Starting services..." } channel = Channel(Nil).new(SERVICES.size) diff --git a/docker/frontend/.nvmrc b/docker/frontend/.nvmrc deleted file mode 100644 index 53a4221..0000000 --- a/docker/frontend/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v16.13.2 diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile index facf062..a5b3442 100644 --- a/docker/frontend/Dockerfile +++ b/docker/frontend/Dockerfile @@ -1,5 +1,5 @@ # Install dependencies only when needed -FROM node:14-alpine AS deps +FROM node:16-alpine AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat WORKDIR /app @@ -7,27 +7,24 @@ COPY package.json yarn.lock ./ RUN yarn install --frozen-lockfile # Rebuild the source code only when needed -FROM node:14-alpine AS builder +FROM node:16-alpine AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . RUN yarn build # Production image, copy all the files and run next -FROM node:14-alpine AS runner +FROM node:16-alpine AS runner WORKDIR /app -ARG BUILD_ENV -ENV NODE_ENV ${BUILD_ENV} - RUN addgroup -g 1001 -S nodejs RUN adduser -S nextjs -u 1001 # You only need to copy next.config.js if you are NOT using the default configuration # COPY --from=builder /app/next.config.js ./ +COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next -COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/package.json ./package.json USER nextjs @@ -39,4 +36,7 @@ EXPOSE 3000 # Uncomment the following line in case you want to disable telemetry. ENV NEXT_TELEMETRY_DISABLED 1 +ARG BUILD_ENV +ENV NODE_ENV ${BUILD_ENV} + CMD ["yarn", "start"] \ No newline at end of file diff --git a/docker/frontend/components/navbar.tsx b/docker/frontend/components/navbar.tsx new file mode 100644 index 0000000..e1a08b7 --- /dev/null +++ b/docker/frontend/components/navbar.tsx @@ -0,0 +1,29 @@ +import Cookies from "js-cookie"; +import Link from "next/link"; + +function Navbar(): JSX.Element { + const isLoggedIn = !!Cookies.get("mentorenwahl_bearer"); + + function handleLogout(event: MouseEvent): void { + event.preventDefault(); + Cookies.remove("mentorenwahl_bearer"); + } + + return ( + + ); +} + +export default Navbar; diff --git a/docker/frontend/layouts/main.tsx b/docker/frontend/layouts/main.tsx new file mode 100644 index 0000000..ebf8287 --- /dev/null +++ b/docker/frontend/layouts/main.tsx @@ -0,0 +1,16 @@ +import Navbar from "../components/navbar"; + +interface MainLayoutProps { + children: React.ReactNode; +} + +function MainLayout({ children }: MainLayoutProps): JSX.Element { + return ( +
Du bist eingeloggt!
; +} export default Home; diff --git a/docker/frontend/pages/login.tsx b/docker/frontend/pages/login.tsx index 398ea51..47fd454 100644 --- a/docker/frontend/pages/login.tsx +++ b/docker/frontend/pages/login.tsx @@ -1,39 +1,40 @@ -import type { NextPage } from "next"; -import type { FormEvent } from "react"; -import { gql } from "@apollo/client"; +import type { FormEvent, FormEventHandler } from "react"; +import { gql, useMutation } from "@apollo/client"; +import Cookies from "js-cookie"; +import Router from "next/router"; -import { client } from "../lib/client"; +import * as cookieNames from "../lib/cookieNames"; -const Login: NextPage = () => { - async function loginUser(event: FormEvent): Promise{error.message}
}