init
This commit is contained in:
commit
d14ed4be15
4
.example.env
Normal file
4
.example.env
Normal file
|
@ -0,0 +1,4 @@
|
|||
POSTGRES_USER=
|
||||
POSTGRES_PASSWORD=
|
||||
|
||||
BACKEND_JWT_SECRET=
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.env
|
20
config/nginx/nginx.conf
Normal file
20
config/nginx/nginx.conf
Normal file
|
@ -0,0 +1,20 @@
|
|||
events {
|
||||
}
|
||||
|
||||
http {
|
||||
server {
|
||||
# location / {
|
||||
# # proxy_set_header Host $host;
|
||||
# # proxy_set_header X-Real-IP $remote_addr;
|
||||
# # proxy_pass http://frontend:3000;
|
||||
# }
|
||||
|
||||
location /graphql {
|
||||
proxy_pass http://backend:8080;
|
||||
}
|
||||
|
||||
location /adminer {
|
||||
proxy_pass http://adminer:8080;
|
||||
}
|
||||
}
|
||||
}
|
43
docker-compose.yml
Normal file
43
docker-compose.yml
Normal file
|
@ -0,0 +1,43 @@
|
|||
services:
|
||||
nginx:
|
||||
container_name: nginx
|
||||
image: nginx:1.20.2-alpine
|
||||
volumes:
|
||||
- ./config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- 80:80
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
db:
|
||||
image: postgres:alpine3.15
|
||||
container_name: db
|
||||
env_file: .env
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- db:/var/lib/postgresql/data
|
||||
|
||||
adminer:
|
||||
image: adminer:4.8.1-standalone
|
||||
container_name: adminer
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ./docker/backend
|
||||
args:
|
||||
BUILD_ENV: production
|
||||
container_name: backend
|
||||
environment:
|
||||
BACKEND_SERVER_PORT: 8080
|
||||
BACKEND_SERVER_HOST: 0.0.0.0
|
||||
BACKEND_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_USER}
|
||||
BACKEND_JWT_SECRET: ${BACKEND_JWT_SECRET}
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
volumes:
|
||||
db:
|
6
docker/backend/.dockerignore
Normal file
6
docker/backend/.dockerignore
Normal file
|
@ -0,0 +1,6 @@
|
|||
/docs/
|
||||
/lib/
|
||||
/bin/
|
||||
/.shards/
|
||||
*.dwarf
|
||||
.ameba.yml
|
6
docker/backend/.gitignore
vendored
Normal file
6
docker/backend/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
/docs/
|
||||
/lib/
|
||||
/bin/
|
||||
/.shards/
|
||||
*.dwarf
|
||||
.ameba.yml
|
44
docker/backend/Dockerfile
Normal file
44
docker/backend/Dockerfile
Normal file
|
@ -0,0 +1,44 @@
|
|||
FROM python:alpine3.15 as pgsanity
|
||||
WORKDIR /app
|
||||
RUN apk add libecpg-dev --no-cache
|
||||
RUN pip3 install pgsanity
|
||||
COPY ./db ./db
|
||||
RUN find -name "*.sql" | xargs pgsanity
|
||||
|
||||
FROM crystallang/crystal:1.2.2-alpine as micrate-deps
|
||||
WORKDIR /app
|
||||
COPY ./micrate/shard.yml ./micrate/shard.lock ./
|
||||
RUN shards install --production
|
||||
|
||||
FROM crystallang/crystal:1.2.2-alpine as micrate-builder
|
||||
ARG BUILD_ENV
|
||||
WORKDIR /app
|
||||
COPY --from=micrate-deps /app/shard.yml /app/shard.lock ./
|
||||
COPY --from=micrate-deps /app/lib lib/
|
||||
COPY ./micrate/src ./src
|
||||
COPY ./scripts ./scripts
|
||||
RUN . ./scripts/build.sh ${BUILD_ENV}
|
||||
|
||||
FROM crystallang/crystal:1.2.2-alpine as deps
|
||||
WORKDIR /app
|
||||
RUN apk add curl --no-cache
|
||||
COPY ./shard.yml ./shard.lock ./
|
||||
RUN shards install
|
||||
|
||||
FROM crystallang/crystal:1.2.2-alpine as builder
|
||||
ARG BUILD_ENV
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/shard.yml /app/shard.lock ./
|
||||
COPY --from=deps /app/lib ./lib
|
||||
COPY --from=deps /app/bin ./bin
|
||||
COPY ./src ./src
|
||||
RUN if [ ${BUILD_ENV} = "development" ]; then ./bin/ameba ./src; fi
|
||||
COPY ./scripts ./scripts
|
||||
RUN . ./scripts/build.sh ${BUILD_ENV}
|
||||
|
||||
FROM scratch as runner
|
||||
COPY --from=micrate-builder /app/bin/micrate .
|
||||
COPY --from=builder /app/bin/mw .
|
||||
COPY --from=pgsanity /app/db ./db
|
||||
EXPOSE 8080
|
||||
CMD [ "/mw" ]
|
79
docker/backend/db/migrations/20211126143138_init.sql
Normal file
79
docker/backend/db/migrations/20211126143138_init.sql
Normal file
|
@ -0,0 +1,79 @@
|
|||
-- +micrate Up
|
||||
-- SQL in section 'Up' is executed when this migration is applied
|
||||
CREATE TYPE user_roles AS ENUM ('Admin', 'Teacher', 'Student');
|
||||
|
||||
CREATE TABLE users(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
firstname TEXT NOT NULL,
|
||||
lastname TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
PASSWORD TEXT NOT NULL,
|
||||
role user_roles NOT NULL,
|
||||
blocked BOOLEAN NOT NULL,
|
||||
UNIQUE (firstname, lastname, email)
|
||||
);
|
||||
|
||||
CREATE TABLE admins(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL UNIQUE REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE teachers(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL UNIQUE REFERENCES users(id),
|
||||
max_students INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE students(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL UNIQUE REFERENCES users(id),
|
||||
skif BOOLEAN NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE
|
||||
users
|
||||
ADD
|
||||
COLUMN admin_id BIGINT UNIQUE REFERENCES admins(id);
|
||||
|
||||
ALTER TABLE
|
||||
users
|
||||
ADD
|
||||
COLUMN teacher_id BIGINT UNIQUE REFERENCES teachers(id);
|
||||
|
||||
ALTER TABLE
|
||||
users
|
||||
ADD
|
||||
COLUMN student_id BIGINT UNIQUE REFERENCES students(id);
|
||||
|
||||
CREATE TABLE votes(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
student_id BIGINT NOT NULL UNIQUE REFERENCES students(id)
|
||||
);
|
||||
|
||||
ALTER TABLE
|
||||
students
|
||||
ADD
|
||||
COLUMN vote_id BIGINT UNIQUE REFERENCES votes(id);
|
||||
|
||||
CREATE TABLE teacher_votes(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
vote_id BIGINT NOT NULL REFERENCES votes(id),
|
||||
teacher_id BIGINT NOT NULL REFERENCES teachers(id),
|
||||
priority INT NOT NULL
|
||||
);
|
||||
|
||||
-- +micrate Down
|
||||
-- SQL section 'Down' is executed when this migration is rolled back
|
||||
DROP TABLE teacher_votes;
|
||||
|
||||
DROP TABLE votes;
|
||||
|
||||
DROP TABLE users;
|
||||
|
||||
DROP TABLE admins;
|
||||
|
||||
DROP TABLE teachers;
|
||||
|
||||
DROP TABLE students;
|
||||
|
||||
DROP TYPE user_roles;
|
5
docker/backend/micrate/.dockerignore
Normal file
5
docker/backend/micrate/.dockerignore
Normal file
|
@ -0,0 +1,5 @@
|
|||
/docs/
|
||||
/lib/
|
||||
/bin/
|
||||
/.shards/
|
||||
*.dwarf
|
5
docker/backend/micrate/.gitignore
vendored
Normal file
5
docker/backend/micrate/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/docs/
|
||||
/lib/
|
||||
/bin/
|
||||
/.shards/
|
||||
*.dwarf
|
14
docker/backend/micrate/shard.lock
Normal file
14
docker/backend/micrate/shard.lock
Normal file
|
@ -0,0 +1,14 @@
|
|||
version: 2.0
|
||||
shards:
|
||||
db:
|
||||
git: https://github.com/crystal-lang/crystal-db.git
|
||||
version: 0.10.1
|
||||
|
||||
micrate:
|
||||
git: https://github.com/juanedi/micrate.git
|
||||
version: 0.12.0
|
||||
|
||||
pg:
|
||||
git: https://github.com/will/crystal-pg.git
|
||||
version: 0.24.0
|
||||
|
19
docker/backend/micrate/shard.yml
Normal file
19
docker/backend/micrate/shard.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
name: micrate
|
||||
version: 0.1.0
|
||||
|
||||
authors:
|
||||
- Dominic Grimm <dominic.grimm@gmail.com>
|
||||
|
||||
targets:
|
||||
micrate:
|
||||
main: src/micrate.cr
|
||||
|
||||
crystal: 1.2.2
|
||||
|
||||
license: MIT
|
||||
|
||||
dependencies:
|
||||
micrate:
|
||||
github: juanedi/micrate
|
||||
pg:
|
||||
github: will/crystal-pg
|
5
docker/backend/micrate/src/micrate.cr
Executable file
5
docker/backend/micrate/src/micrate.cr
Executable file
|
@ -0,0 +1,5 @@
|
|||
require "micrate"
|
||||
require "pg"
|
||||
|
||||
Micrate::DB.connection_url = ENV["BACKEND_DATABASE_URL"]?
|
||||
Micrate::Cli.run
|
14
docker/backend/scripts/build.sh
Normal file
14
docker/backend/scripts/build.sh
Normal file
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
BASE_FLAGS="--production --static --verbose -s -p -t"
|
||||
DEV_FLAGS="$BASE_FLAGS"
|
||||
PROD_FLAGS="--release --no-debug $BASE_FLAGS"
|
||||
|
||||
echo "Building targets in '$1' mode..."
|
||||
if [ "$1" = "development" ]; then
|
||||
# shellcheck disable=SC2086
|
||||
time shards build $DEV_FLAGS
|
||||
else
|
||||
# shellcheck disable=SC2086
|
||||
time shards build $PROD_FLAGS
|
||||
fi
|
66
docker/backend/shard.lock
Normal file
66
docker/backend/shard.lock
Normal file
|
@ -0,0 +1,66 @@
|
|||
version: 2.0
|
||||
shards:
|
||||
CrystalEmail:
|
||||
git: https://git.sceptique.eu/Sceptique/CrystalEmail
|
||||
version: 0.2.6+git.commit.f217992c51048b3f94f4e064cd6c5123e32a1e27
|
||||
|
||||
ameba:
|
||||
git: https://github.com/crystal-ameba/ameba.git
|
||||
version: 0.14.3
|
||||
|
||||
bindata:
|
||||
git: https://github.com/spider-gazelle/bindata.git
|
||||
version: 1.9.1
|
||||
|
||||
commander:
|
||||
git: https://github.com/mrrooijen/commander.git
|
||||
version: 0.4.0
|
||||
|
||||
compiled_license:
|
||||
git: https://github.com/grimmigerfuchs/compiled_license.git
|
||||
version: 2.0.0
|
||||
|
||||
crystal-argon2:
|
||||
git: https://github.com/axentro/crystal-argon2.git
|
||||
version: 0.1.3
|
||||
|
||||
db:
|
||||
git: https://github.com/crystal-lang/crystal-db.git
|
||||
version: 0.10.1
|
||||
|
||||
granite:
|
||||
git: https://github.com/amberframework/granite.git
|
||||
version: 0.23.0
|
||||
|
||||
graphql:
|
||||
git: https://github.com/graphql-crystal/graphql.git
|
||||
version: 0.3.2+git.commit.8c6dc73c0c898ca511d9d12efefca7c837c25946
|
||||
|
||||
jwt:
|
||||
git: https://github.com/crystal-community/jwt.git
|
||||
version: 1.6.0
|
||||
|
||||
openssl_ext:
|
||||
git: https://github.com/spider-gazelle/openssl_ext.git
|
||||
version: 2.1.5
|
||||
|
||||
pg:
|
||||
git: https://github.com/will/crystal-pg.git
|
||||
version: 0.24.0
|
||||
|
||||
pretty:
|
||||
git: https://github.com/maiha/pretty.cr.git
|
||||
version: 1.1.1
|
||||
|
||||
seg:
|
||||
git: https://github.com/soveran/seg.git
|
||||
version: 0.1.0+git.commit.b404c986b5a894830878cd6b296df53ef34d9cfa
|
||||
|
||||
shard:
|
||||
git: https://github.com/maiha/shard.cr.git
|
||||
version: 0.3.1
|
||||
|
||||
toro:
|
||||
git: https://github.com/soveran/toro.git
|
||||
version: 0.4.2+git.commit.d2103dddcc9cc757e6b82613e040b1d50663a2e6
|
||||
|
40
docker/backend/shard.yml
Normal file
40
docker/backend/shard.yml
Normal file
|
@ -0,0 +1,40 @@
|
|||
name: mw
|
||||
version: 0.1.0
|
||||
|
||||
authors:
|
||||
- Dominic Grimm <dominic.grimm@gmail.com>
|
||||
|
||||
targets:
|
||||
mw:
|
||||
main: src/app.cr
|
||||
|
||||
crystal: 1.2.2
|
||||
|
||||
dependencies:
|
||||
granite:
|
||||
github: amberframework/granite
|
||||
pg:
|
||||
github: will/crystal-pg
|
||||
crystal-argon2:
|
||||
github: Axentro/crystal-argon2
|
||||
graphql:
|
||||
github: graphql-crystal/graphql
|
||||
branch: master
|
||||
jwt:
|
||||
github: crystal-community/jwt
|
||||
CrystalEmail:
|
||||
git: https://git.sceptique.eu/Sceptique/CrystalEmail
|
||||
branch: master
|
||||
shard:
|
||||
github: maiha/shard.cr
|
||||
toro:
|
||||
github: soveran/toro
|
||||
branch: master
|
||||
commander:
|
||||
github: mrrooijen/commander
|
||||
compiled_license:
|
||||
github: grimmigerFuchs/compiled_license
|
||||
|
||||
development_dependencies:
|
||||
ameba:
|
||||
github: crystal-ameba/ameba
|
94
docker/backend/src/app.cr
Normal file
94
docker/backend/src/app.cr
Normal file
|
@ -0,0 +1,94 @@
|
|||
require "commander"
|
||||
require "./mw.cr"
|
||||
|
||||
def input(prompt : String) : String
|
||||
print prompt
|
||||
(gets || "").chomp.strip
|
||||
end
|
||||
|
||||
cli = Commander::Command.new do |cmd|
|
||||
cmd.use = "mw"
|
||||
cmd.long = "Mentorenwahl"
|
||||
|
||||
cmd.run do
|
||||
MW.run
|
||||
end
|
||||
|
||||
cmd.commands.add do |c|
|
||||
c.use = "version"
|
||||
c.long = "Prints the current version"
|
||||
|
||||
c.run do
|
||||
puts MW::VERSION
|
||||
end
|
||||
end
|
||||
|
||||
cmd.commands.add do |c|
|
||||
c.use = "authors"
|
||||
c.long = "Prints the authors"
|
||||
|
||||
c.run do
|
||||
puts MW::AUTHORS.join(",\n")
|
||||
end
|
||||
end
|
||||
|
||||
cmd.commands.add do |c|
|
||||
c.use = "licenses"
|
||||
c.long = "Prints the licenses of libraries used"
|
||||
|
||||
c.run do
|
||||
puts MW::LICENSES
|
||||
end
|
||||
end
|
||||
|
||||
cmd.commands.add do |c|
|
||||
c.use = "seed"
|
||||
c.long = "Seeds the database with required data"
|
||||
|
||||
c.run do
|
||||
puts "Seeding database with admin user..."
|
||||
# firstname = input "Firstname: "
|
||||
# lastname = input "Lastname: "
|
||||
# email = input "Email: "
|
||||
# password = input "Password: "
|
||||
# password_confirmation = input "Password confirmation: "
|
||||
data = {
|
||||
"firstname" => input("Firstname: "),
|
||||
"lastname" => input("Lastname: "),
|
||||
"email" => input("Email: "),
|
||||
"password" => MW::Auth.hash_password(input("Password: ")),
|
||||
"role" => MW::Db::UserRole::Admin.to_s,
|
||||
}
|
||||
password_confirmation = input("Password confirmation: ")
|
||||
|
||||
if data.values.any?(&.empty?)
|
||||
abort "Values can't be empty!"
|
||||
elsif !MW::Auth.verify_password?(password_confirmation, data["password"])
|
||||
abort "Passwords do not match!"
|
||||
end
|
||||
|
||||
puts "---"
|
||||
data.each { |k, v| puts "#{k.capitalize}: #{v}" }
|
||||
puts "---"
|
||||
|
||||
unless input("Are you sure? (y/n) ") == "y"
|
||||
abort "Aborted!"
|
||||
end
|
||||
|
||||
puts "Seeding database with admin user..."
|
||||
|
||||
user = MW::Db::User.create!(data)
|
||||
admin = MW::Db::Admin.create!(user_id: user.id)
|
||||
|
||||
puts "Done!"
|
||||
|
||||
puts "---"
|
||||
puts "User id: #{user.id}"
|
||||
puts "Admin id: #{admin.id}"
|
||||
puts "Token: #{MW::Auth.create_user_jwt(user_id: user.id.not_nil!)}"
|
||||
puts "---"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Commander.run(cli, ARGV)
|
1
docker/backend/src/mw.cr
Normal file
1
docker/backend/src/mw.cr
Normal file
|
@ -0,0 +1 @@
|
|||
require "./mw/*"
|
43
docker/backend/src/mw/auth.cr
Normal file
43
docker/backend/src/mw/auth.cr
Normal file
|
@ -0,0 +1,43 @@
|
|||
require "crystal-argon2"
|
||||
require "jwt"
|
||||
|
||||
module MW
|
||||
module Auth
|
||||
extend self
|
||||
|
||||
BEARER = "Bearer "
|
||||
|
||||
def hash_password(password : String) : String
|
||||
Argon2::Password.create(password)
|
||||
end
|
||||
|
||||
def verify_password?(password : String, hash : String) : Bool
|
||||
!!Argon2::Password.verify_password(password, hash)
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
private def create_jwt(data, expiration : Int) : String
|
||||
payload = {
|
||||
"data" => data.to_h,
|
||||
"exp" => expiration,
|
||||
}
|
||||
|
||||
JWT.encode(payload.to_h, ENV_REQUESTER["BACKEND_JWT_SECRET"], JWT::Algorithm::HS256)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def decode_jwt(jwt : String) : JSON::Any
|
||||
JWT.decode(jwt, ENV_REQUESTER["BACKEND_JWT_SECRET"], JWT::Algorithm::HS256)[0]
|
||||
end
|
||||
|
||||
def decode_jwt?(jwt : String) : JSON::Any?
|
||||
decode_jwt(jwt)
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
5
docker/backend/src/mw/authors.cr
Normal file
5
docker/backend/src/mw/authors.cr
Normal file
|
@ -0,0 +1,5 @@
|
|||
require "shard"
|
||||
|
||||
module MW
|
||||
AUTHORS = Shard.authors
|
||||
end
|
88
docker/backend/src/mw/context.cr
Normal file
88
docker/backend/src/mw/context.cr
Normal file
|
@ -0,0 +1,88 @@
|
|||
require "http/request"
|
||||
require "graphql"
|
||||
require "granite"
|
||||
|
||||
module MW
|
||||
class Context < GraphQL::Context
|
||||
getter user : Db::User?
|
||||
getter role : Schema::UserRole?
|
||||
getter external : (Db::Admin | Db::Teacher | Db::Student)?
|
||||
|
||||
# ameba:disable Metrics/CyclomaticComplexity
|
||||
def initialize(request : HTTP::Request, *rest)
|
||||
super(*rest)
|
||||
|
||||
token = request.headers["Authorization"]?
|
||||
if token && token[..Auth::BEARER.size - 1] == Auth::BEARER
|
||||
payload = Auth.decode_jwt?(token[Auth::BEARER.size..])
|
||||
return unless payload
|
||||
|
||||
data = payload["data"].as_h
|
||||
@user = Db::User.find(data["user_id"].as_i)
|
||||
return if @user.nil? || @user.not_nil!.blocked
|
||||
|
||||
if @user
|
||||
tmp_role = Schema::UserRole.parse?(@user.as(Db::User).role).not_nil!
|
||||
if tmp_role
|
||||
@external =
|
||||
case Schema::UserRole.parse?(@user.not_nil!.role)
|
||||
when Schema::UserRole::Admin
|
||||
@user.not_nil!.admin
|
||||
when Schema::UserRole::Teacher
|
||||
@user.not_nil!.teacher
|
||||
when Schema::UserRole::Student
|
||||
@user.not_nil!.student
|
||||
end
|
||||
@role = tmp_role if @external
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def authenticated? : Bool
|
||||
!(@role.nil? && @external.nil?)
|
||||
end
|
||||
|
||||
def authenticated! : Bool
|
||||
raise "Not authenticated" unless authenticated?
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def role?(role : Schema::UserRole) : Bool
|
||||
@role == role == case @external
|
||||
when Db::Admin
|
||||
Schema::UserRole::Admin
|
||||
when Db::Teacher
|
||||
Schema::UserRole::Teacher
|
||||
when Db::Student
|
||||
Schema::UserRole::Student
|
||||
end
|
||||
end
|
||||
|
||||
def role!(role : Schema::UserRole) : Bool
|
||||
raise "Invalid permissions" unless role? role
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def admin? : Bool
|
||||
role? Schema::UserRole::Admin
|
||||
end
|
||||
|
||||
def admin! : Bool
|
||||
role! Schema::UserRole::Admin
|
||||
end
|
||||
|
||||
def self.db_eq_role?(external : Granite::Base, role : Schema::UserRole) : Bool
|
||||
role == case external
|
||||
when Db::Admin
|
||||
Schema::UserRole::Admin
|
||||
when Db::Teacher
|
||||
Schema::UserRole::Teacher
|
||||
when Db::Student
|
||||
Schema::UserRole::Student
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
10
docker/backend/src/mw/db.cr
Normal file
10
docker/backend/src/mw/db.cr
Normal file
|
@ -0,0 +1,10 @@
|
|||
require "granite"
|
||||
require "granite/adapter/pg"
|
||||
|
||||
require "./db/*"
|
||||
|
||||
module MW
|
||||
module Db
|
||||
Granite::Connections << Granite::Adapter::Pg.new(name: "pg", url: ENV_REQUESTER["BACKEND_DATABASE_URL"])
|
||||
end
|
||||
end
|
13
docker/backend/src/mw/db/admin.cr
Normal file
13
docker/backend/src/mw/db/admin.cr
Normal file
|
@ -0,0 +1,13 @@
|
|||
require "granite"
|
||||
|
||||
module MW
|
||||
module Db
|
||||
class Admin < Granite::Base
|
||||
table admins
|
||||
|
||||
belongs_to :user
|
||||
|
||||
column id : Int64, primary: true
|
||||
end
|
||||
end
|
||||
end
|
15
docker/backend/src/mw/db/student.cr
Normal file
15
docker/backend/src/mw/db/student.cr
Normal file
|
@ -0,0 +1,15 @@
|
|||
require "granite"
|
||||
|
||||
module MW
|
||||
module Db
|
||||
class Student < Granite::Base
|
||||
table students
|
||||
|
||||
belongs_to :user
|
||||
has_one :vote
|
||||
|
||||
column id : Int64, primary: true
|
||||
column skif : Bool
|
||||
end
|
||||
end
|
||||
end
|
15
docker/backend/src/mw/db/teacher.cr
Normal file
15
docker/backend/src/mw/db/teacher.cr
Normal file
|
@ -0,0 +1,15 @@
|
|||
require "granite"
|
||||
|
||||
module MW
|
||||
module Db
|
||||
class Teacher < Granite::Base
|
||||
table teachers
|
||||
|
||||
belongs_to :user
|
||||
has_many teacher_votes : TeacherVote
|
||||
|
||||
column id : Int64, primary: true
|
||||
column max_students : Int32
|
||||
end
|
||||
end
|
||||
end
|
31
docker/backend/src/mw/db/teacher_vote.cr
Normal file
31
docker/backend/src/mw/db/teacher_vote.cr
Normal file
|
@ -0,0 +1,31 @@
|
|||
require "granite"
|
||||
|
||||
module MW
|
||||
module Db
|
||||
class TeacherVote < Granite::Base
|
||||
table teacher_votes
|
||||
|
||||
belongs_to :vote
|
||||
belongs_to :teacher
|
||||
|
||||
column id : Int64, primary: true
|
||||
column priority : Int32
|
||||
|
||||
validate :teacher, "must be present" do |teacher_vote|
|
||||
!teacher_vote.teacher.nil?
|
||||
end
|
||||
|
||||
validate :teacher, "must be student unique" do |teacher_vote|
|
||||
self.where(vote_id: teacher_vote.vote.id, teacher_id: teacher_vote.teacher.not_nil!.id).count == 0
|
||||
end
|
||||
|
||||
validate :priority, "must be greater than 0" do |teacher_vote|
|
||||
teacher_vote.priority > 0
|
||||
end
|
||||
|
||||
validate :priority, "must be less than the number of teachers" do |teacher_vote|
|
||||
teacher_vote.priority < Teacher.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
47
docker/backend/src/mw/db/user.cr
Normal file
47
docker/backend/src/mw/db/user.cr
Normal file
|
@ -0,0 +1,47 @@
|
|||
require "CrystalEmail"
|
||||
require "granite"
|
||||
|
||||
module MW
|
||||
module Db
|
||||
class User < Granite::Base
|
||||
table users
|
||||
|
||||
has_one :admin
|
||||
has_one :teacher
|
||||
has_one :student
|
||||
|
||||
column id : Int64, primary: true
|
||||
column firstname : String
|
||||
column lastname : String
|
||||
column email : String
|
||||
column password : String
|
||||
column role : String
|
||||
column blocked : Bool = false
|
||||
|
||||
validate :email, "needs to be an email address" do |user|
|
||||
CrystalEmail::Rfc5322::Public.validates?(user.email)
|
||||
end
|
||||
|
||||
validate :role, "needs to be a valid role" do |user|
|
||||
UserRole.parse?(user.role).in?(UserRole.values)
|
||||
end
|
||||
|
||||
validate :role, "user external needs to be a valid role" do |user|
|
||||
if user.admin.nil? && user.teacher.nil? && user.student.nil?
|
||||
true
|
||||
else
|
||||
!!case UserRole.parse(user.role)
|
||||
when UserRole::Admin
|
||||
user.admin && user.teacher.nil? && user.student.nil?
|
||||
when UserRole::Teacher
|
||||
user.admin.nil? && user.teacher && user.student.nil?
|
||||
when UserRole::Student
|
||||
user.admin.nil? && user.teacher.nil? && user.student
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
9
docker/backend/src/mw/db/user_role.cr
Normal file
9
docker/backend/src/mw/db/user_role.cr
Normal file
|
@ -0,0 +1,9 @@
|
|||
module MW
|
||||
module Db
|
||||
enum UserRole
|
||||
Admin
|
||||
Teacher
|
||||
Student
|
||||
end
|
||||
end
|
||||
end
|
14
docker/backend/src/mw/db/vote.cr
Normal file
14
docker/backend/src/mw/db/vote.cr
Normal file
|
@ -0,0 +1,14 @@
|
|||
require "granite"
|
||||
|
||||
module MW
|
||||
module Db
|
||||
class Vote < Granite::Base
|
||||
table votes
|
||||
|
||||
belongs_to :student
|
||||
has_many teacher_votes : TeacherVote
|
||||
|
||||
column id : Int64, primary: true
|
||||
end
|
||||
end
|
||||
end
|
43
docker/backend/src/mw/env_requester.cr
Normal file
43
docker/backend/src/mw/env_requester.cr
Normal file
|
@ -0,0 +1,43 @@
|
|||
module MW
|
||||
class EnvRequester
|
||||
private property keys
|
||||
|
||||
def initialize(@keys = {} of String => String?)
|
||||
end
|
||||
|
||||
def initialize(keys : Array(String))
|
||||
@keys = {} of String => String?
|
||||
keys.each { |k| self.<< k }
|
||||
end
|
||||
|
||||
def <<(key : String) : self
|
||||
@keys[key] = ENV[key]?
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def []?(key : String) : String?
|
||||
if @keys.has_key?(key)
|
||||
@keys[key]?
|
||||
end
|
||||
end
|
||||
|
||||
def [](key : String) : String
|
||||
if @keys.has_key?(key)
|
||||
val = @keys[key]?
|
||||
raise "ENV[#{key}] is nil" unless val
|
||||
|
||||
val
|
||||
else
|
||||
raise "No such key: #{key}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ENV_REQUESTER = EnvRequester.new([
|
||||
"BACKEND_DATABASE_URL",
|
||||
"BACKEND_ADMIN_EMAIL",
|
||||
"BACKEND_ADMIN_PASSWORD",
|
||||
"BACKEND_JWT_SECRET",
|
||||
])
|
||||
end
|
5
docker/backend/src/mw/licenses.cr
Normal file
5
docker/backend/src/mw/licenses.cr
Normal file
|
@ -0,0 +1,5 @@
|
|||
require "compiled_license"
|
||||
|
||||
module MW
|
||||
LICENSES = CompiledLicense::LICENSES
|
||||
end
|
13
docker/backend/src/mw/run.cr
Normal file
13
docker/backend/src/mw/run.cr
Normal file
|
@ -0,0 +1,13 @@
|
|||
require "http/server"
|
||||
|
||||
module MW
|
||||
extend self
|
||||
|
||||
def run : Nil
|
||||
Server.run(8080, [HTTP::LogHandler.new, HTTP::ErrorHandler.new]) do |server|
|
||||
address = server.bind_tcp("0.0.0.0", 8080, true)
|
||||
puts "Listening on http://#{address}"
|
||||
server.listen
|
||||
end
|
||||
end
|
||||
end
|
10
docker/backend/src/mw/schema.cr
Normal file
10
docker/backend/src/mw/schema.cr
Normal file
|
@ -0,0 +1,10 @@
|
|||
require "graphql"
|
||||
|
||||
require "./schema/helpers"
|
||||
require "./schema/*"
|
||||
|
||||
module MW
|
||||
module Schema
|
||||
SCHEMA = GraphQL::Schema.new(Query.new, Mutation.new)
|
||||
end
|
||||
end
|
26
docker/backend/src/mw/schema/admin.cr
Normal file
26
docker/backend/src/mw/schema/admin.cr
Normal file
|
@ -0,0 +1,26 @@
|
|||
require "graphql"
|
||||
|
||||
module MW
|
||||
module Schema
|
||||
@[GraphQL::Object]
|
||||
class Admin < GraphQL::BaseObject
|
||||
include Helpers::DbObject
|
||||
|
||||
db_object Db::Admin
|
||||
|
||||
@[GraphQL::Field]
|
||||
def user : User
|
||||
User.new(find!.user)
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::InputObject]
|
||||
class AdminCreateInput < GraphQL::BaseInputObject
|
||||
getter user_id
|
||||
|
||||
@[GraphQL::Field]
|
||||
def initialize(@user_id : Int32)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
65
docker/backend/src/mw/schema/helpers.cr
Normal file
65
docker/backend/src/mw/schema/helpers.cr
Normal file
|
@ -0,0 +1,65 @@
|
|||
require "graphql"
|
||||
|
||||
module MW
|
||||
module Schema
|
||||
module Helpers
|
||||
module ObjectMacros
|
||||
macro field(type)
|
||||
property {{ type.var }} {% if type.value %} = {{ type.value }}{% end %}
|
||||
|
||||
@[GraphQL::Field]
|
||||
def {{ type.var }} : {{ type.type }}
|
||||
@{{ type.var }}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ObjectDbInit
|
||||
macro db_init(type)
|
||||
def initialize(obj : {{ type }})
|
||||
initialize(obj.id.not_nil!)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ObjectFinders
|
||||
macro finders(type)
|
||||
def find : {{ type }}?
|
||||
{{ type }}.find(@id)
|
||||
end
|
||||
|
||||
def find! : {{ type }}
|
||||
obj = find
|
||||
raise "#{{{ type }}} not found" unless obj
|
||||
|
||||
obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module DbObject
|
||||
macro db_object(type)
|
||||
include ::MW::Schema::Helpers::ObjectDbInit
|
||||
include ::MW::Schema::Helpers::ObjectFinders
|
||||
|
||||
db_init {{ type }}
|
||||
finders {{ type }}
|
||||
|
||||
property id
|
||||
|
||||
def initialize(@id : Int32)
|
||||
end
|
||||
|
||||
def initialize(obj : {{ type }})
|
||||
@id = obj.id.not_nil!.to_i
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def id : Int32
|
||||
@id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
136
docker/backend/src/mw/schema/mutation.cr
Normal file
136
docker/backend/src/mw/schema/mutation.cr
Normal file
|
@ -0,0 +1,136 @@
|
|||
require "graphql"
|
||||
|
||||
module MW
|
||||
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)
|
||||
|
||||
LoginPayload.new(
|
||||
user: User.new(user),
|
||||
token: Auth.create_user_jwt(user.id.not_nil!.to_i),
|
||||
)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def create_user(context : Context, input : UserCreateInput) : User
|
||||
context.admin!
|
||||
|
||||
user = Db::User.create!(
|
||||
firstname: input.firstname,
|
||||
lastname: input.lastname,
|
||||
email: input.email,
|
||||
password: Auth.hash_password(input.password),
|
||||
role: input.role.to_s,
|
||||
blocked: input.blocked,
|
||||
)
|
||||
if input.role
|
||||
case input.role
|
||||
when UserRole::Teacher
|
||||
user.teacher = Db::Teacher.create!(user_id: user.id, max_students: input.teacher.not_nil!.max_students)
|
||||
when UserRole::Student
|
||||
user.student = Db::Student.create!(user_id: user.id, skif: input.student.not_nil!.skif)
|
||||
end
|
||||
user.save!
|
||||
end
|
||||
|
||||
User.new(user)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def delete_user(context : Context, id : Int32) : Int32
|
||||
context.admin!
|
||||
|
||||
user = Db::User.find!(id)
|
||||
user.destroy!
|
||||
|
||||
id
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def create_admin(context : Context, input : AdminCreateInput) : Admin
|
||||
context.admin!
|
||||
|
||||
admin = Db::Admin.create!(user_id: input.user_id)
|
||||
Admin.new(admin)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def delete_admin(context : Context, id : Int32) : Int32
|
||||
context.admin!
|
||||
|
||||
admin = Db::Admin.find!(id)
|
||||
admin.destroy!
|
||||
|
||||
id
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def create_teacher(context : Context, input : TeacherCreateInput) : Teacher
|
||||
context.admin!
|
||||
|
||||
teacher = Db::Teacher.create!(user_id: input.user_id, max_students: input.max_students)
|
||||
Teacher.new(teacher)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def delete_teacher(context : Context, id : Int32) : Int32
|
||||
context.admin!
|
||||
|
||||
teacher = Db::Teacher.find!(id)
|
||||
teacher.destroy!
|
||||
|
||||
id
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def create_student(context : Context, input : StudentCreateInput) : Student
|
||||
context.admin!
|
||||
|
||||
user = Db::User.find!(input.user_id)
|
||||
raise "User not a student" unless UserRole.parse(user.role) == UserRole::Student
|
||||
|
||||
student = Db::Student.create!(user_id: user.id)
|
||||
Student.new(student)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def delete_student(context : Context, id : Int32) : Int32
|
||||
context.admin!
|
||||
|
||||
student = Db::Student.find!(id)
|
||||
student.destroy!
|
||||
|
||||
id
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def create_vote(context : Context, input : VoteCreateInput) : Vote
|
||||
# context.admin!
|
||||
|
||||
# student = Db::Student.find!(input.student_id)
|
||||
# # student.vote = Db::Vote.new(student_id: student.id)
|
||||
# # student.save!
|
||||
# pp! Db::Vote.create!(student_id: student.id.not_nil!)
|
||||
# pp! student
|
||||
# pp! student.vote
|
||||
# input.teacher_ids.not_nil!.each_with_index do |t_id, i|
|
||||
# Db::TeacherVote.create!(vote_id: student.vote.not_nil!.id.not_nil!, teacher_id: t_id, priority: i)
|
||||
# end if input.teacher_ids
|
||||
# pp! student.vote.not_nil!.teacher_votes.to_a
|
||||
|
||||
# Vote.new(student.vote.not_nil!)
|
||||
|
||||
context.role! UserRole::Student
|
||||
|
||||
student = context.external.not_nil!.as(Db::Student)
|
||||
vote = Db::Vote.create!(student_id: student.id)
|
||||
|
||||
Vote.new(vote)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
87
docker/backend/src/mw/schema/query.cr
Normal file
87
docker/backend/src/mw/schema/query.cr
Normal file
|
@ -0,0 +1,87 @@
|
|||
require "graphql"
|
||||
|
||||
module MW
|
||||
module Schema
|
||||
@[GraphQL::Object]
|
||||
class Query < GraphQL::BaseQuery
|
||||
@[GraphQL::Field]
|
||||
def ok : Bool
|
||||
true
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def me(context : Context) : User
|
||||
context.authenticated!
|
||||
|
||||
User.new(context.user.not_nil!)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def user(context : Context, id : Int32) : User
|
||||
context.admin!
|
||||
|
||||
User.new(id)
|
||||
end
|
||||
|
||||
@[GraphQL::Field({UserRole::Admin})]
|
||||
def users(context : Context) : Array(User)
|
||||
puts "AUTHORIZED ALLOWED ROLES: #{{{ @def.annotation(GraphQL::Field)[0] }}}"
|
||||
context.admin!
|
||||
|
||||
Db::User.all.map { |user| User.new(user) }
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def admin(context : Context, id : Int32) : Admin
|
||||
context.admin!
|
||||
|
||||
Admin.new(Db::Admin.find!(id))
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def admins(context : Context) : Array(Admin)
|
||||
context.admin!
|
||||
|
||||
Db::Admin.all.map { |admin| Admin.new(admin) }
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def teacher(id : Int32) : Teacher
|
||||
Teacher.new(Db::Teacher.find!(id))
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def teachers : Array(Teacher)
|
||||
Db::Teacher.all.map { |teacher| Teacher.new(teacher) }
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def student(context : Context, id : Int32) : Student
|
||||
context.admin!
|
||||
|
||||
Student.new(Db::Student.find!(id))
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def students(context : Context) : Array(Student)
|
||||
context.admin!
|
||||
|
||||
Db::Student.all.map { |student| Student.new(student) }
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def vote(context : Context, id : Int32) : Vote
|
||||
context.admin!
|
||||
|
||||
Vote.new(Db::Vote.find!(id))
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def votes(context : Context) : Array(Vote)
|
||||
context.admin!
|
||||
|
||||
Db::Vote.all.map { |vote| Vote.new(vote) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
48
docker/backend/src/mw/schema/student.cr
Normal file
48
docker/backend/src/mw/schema/student.cr
Normal file
|
@ -0,0 +1,48 @@
|
|||
require "graphql"
|
||||
|
||||
module MW
|
||||
module Schema
|
||||
@[GraphQL::Object]
|
||||
class Student < GraphQL::BaseObject
|
||||
include Helpers::DbObject
|
||||
|
||||
db_object Db::Student
|
||||
|
||||
@[GraphQL::Field]
|
||||
def user : User
|
||||
User.new(find!.user)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def skif : Bool
|
||||
find!.skif
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def vote : Vote?
|
||||
vote = find!.vote
|
||||
|
||||
Vote.new(vote) if vote
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::InputObject]
|
||||
class StudentInput < GraphQL::BaseInputObject
|
||||
getter skif
|
||||
|
||||
@[GraphQL::Field]
|
||||
def initialize(@skif : Bool)
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::InputObject]
|
||||
class StudentCreateInput < StudentInput
|
||||
getter user_id
|
||||
|
||||
@[GraphQL::Field]
|
||||
def initialize(@user_id : Int32, skif : Bool)
|
||||
super(skif)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
43
docker/backend/src/mw/schema/teacher.cr
Normal file
43
docker/backend/src/mw/schema/teacher.cr
Normal file
|
@ -0,0 +1,43 @@
|
|||
require "graphql"
|
||||
|
||||
module MW
|
||||
module Schema
|
||||
@[GraphQL::Object]
|
||||
class Teacher < GraphQL::BaseObject
|
||||
include Helpers::DbObject
|
||||
|
||||
db_object Db::Teacher
|
||||
|
||||
@[GraphQL::Field]
|
||||
def user : User
|
||||
User.new(find!.user)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def max_students(context : Context) : Int32
|
||||
context.admin!
|
||||
|
||||
find!.max_students
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::InputObject]
|
||||
class TeacherInput < GraphQL::BaseInputObject
|
||||
getter max_students
|
||||
|
||||
@[GraphQL::Field]
|
||||
def initialize(@max_students : Int32)
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::InputObject]
|
||||
class TeacherCreateInput < TeacherInput
|
||||
getter user_id
|
||||
|
||||
@[GraphQL::Field]
|
||||
def initialize(@user_id : Int32, max_students : Int32)
|
||||
super(max_students)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
33
docker/backend/src/mw/schema/teacher_vote.cr
Normal file
33
docker/backend/src/mw/schema/teacher_vote.cr
Normal file
|
@ -0,0 +1,33 @@
|
|||
require "graphql"
|
||||
|
||||
module MW
|
||||
module Schema
|
||||
@[GraphQL::Object]
|
||||
class TeacherVote < GraphQL::BaseObject
|
||||
include Helpers::DbObject
|
||||
|
||||
db_object Db::TeacherVote
|
||||
|
||||
@[GraphQL::Field]
|
||||
def teacher : Teacher
|
||||
Teacher.new(find!.teacher.not_nil!)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def priority : Int32
|
||||
find!.priority
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::InputObject]
|
||||
class TeacherVoteCreateInput < GraphQL::BaseInputObject
|
||||
getter vote_id
|
||||
getter teacher_id
|
||||
getter priority
|
||||
|
||||
@[GraphQL::Field]
|
||||
def initialize(@vote_id : Int32, @teacher_id : Int32, @priority : Int32)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
157
docker/backend/src/mw/schema/user.cr
Normal file
157
docker/backend/src/mw/schema/user.cr
Normal file
|
@ -0,0 +1,157 @@
|
|||
require "graphql"
|
||||
|
||||
module MW
|
||||
module Schema
|
||||
@[GraphQL::Enum]
|
||||
enum UserRole
|
||||
Admin
|
||||
Teacher
|
||||
Student
|
||||
end
|
||||
|
||||
@[GraphQL::Object]
|
||||
class User < GraphQL::BaseObject
|
||||
include Helpers::DbObject
|
||||
|
||||
db_object Db::User
|
||||
|
||||
@[GraphQL::Field]
|
||||
def firstname : String
|
||||
find!.firstname
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def lastname : String
|
||||
find!.lastname
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def email : String
|
||||
find!.email
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def role : UserRole
|
||||
role = Db::UserRole.parse(find!.role)
|
||||
case role
|
||||
when Db::UserRole::Admin
|
||||
UserRole::Admin
|
||||
when Db::UserRole::Teacher
|
||||
UserRole::Teacher
|
||||
when Db::UserRole::Student
|
||||
UserRole::Student
|
||||
else
|
||||
raise "Unknown role: #{role}"
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def external_id : Int32?
|
||||
case Db::UserRole.parse(find!.role)
|
||||
when Db::UserRole::Admin
|
||||
find!.admin
|
||||
when Db::UserRole::Teacher
|
||||
find!.teacher
|
||||
when Db::UserRole::Student
|
||||
find!.student
|
||||
end.not_nil!.id.not_nil!.to_i
|
||||
rescue NilAssertionError
|
||||
nil
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def admin : Admin?
|
||||
admin = find!.admin
|
||||
if admin
|
||||
Admin.new(admin)
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def teacher : Teacher?
|
||||
teacher = find!.teacher
|
||||
if teacher
|
||||
Teacher.new(teacher)
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def student : Student?
|
||||
student = find!.student
|
||||
if student
|
||||
Student.new(student)
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def blocked : Bool
|
||||
find!.blocked
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::InputObject]
|
||||
class UserCreateInput < GraphQL::BaseInputObject
|
||||
getter firstname
|
||||
getter lastname
|
||||
getter email
|
||||
getter password
|
||||
getter role
|
||||
getter teacher
|
||||
getter student
|
||||
getter blocked
|
||||
|
||||
@[GraphQL::Field]
|
||||
def initialize(
|
||||
@firstname : String,
|
||||
@lastname : String,
|
||||
@email : String,
|
||||
@password : String,
|
||||
@role : UserRole,
|
||||
@teacher : TeacherInput? = nil,
|
||||
@student : StudentInput? = nil,
|
||||
@blocked : Bool = false
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::InputObject]
|
||||
class LoginInput < GraphQL::BaseInputObject
|
||||
getter email
|
||||
getter password
|
||||
|
||||
@[GraphQL::Field]
|
||||
def initialize(
|
||||
@email : String,
|
||||
@password : String
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::Object]
|
||||
class LoginPayload < GraphQL::BaseObject
|
||||
property user
|
||||
property token
|
||||
|
||||
def initialize(
|
||||
@user : User,
|
||||
@token : String
|
||||
)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def user : User
|
||||
@user
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def token : String
|
||||
@token
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def bearer : String
|
||||
Auth::BEARER + @token
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
31
docker/backend/src/mw/schema/vote.cr
Normal file
31
docker/backend/src/mw/schema/vote.cr
Normal file
|
@ -0,0 +1,31 @@
|
|||
require "graphql"
|
||||
|
||||
module MW
|
||||
module Schema
|
||||
@[GraphQL::Object]
|
||||
class Vote < GraphQL::BaseObject
|
||||
include Helpers::DbObject
|
||||
|
||||
db_object Db::Vote
|
||||
|
||||
@[GraphQL::Field]
|
||||
def student : Student
|
||||
Student.new(find!.student)
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
def teacher_votes : Array(TeacherVote)
|
||||
find!.teacher_votes.map { |tv| TeacherVote.new(tv) }
|
||||
end
|
||||
end
|
||||
|
||||
@[GraphQL::InputObject]
|
||||
class VoteCreateInput < GraphQL::BaseInputObject
|
||||
getter teacher_ids
|
||||
|
||||
@[GraphQL::Field]
|
||||
def initialize(@teacher_ids : Array(String))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
31
docker/backend/src/mw/server.cr
Normal file
31
docker/backend/src/mw/server.cr
Normal file
|
@ -0,0 +1,31 @@
|
|||
require "toro"
|
||||
require "json"
|
||||
|
||||
module MW
|
||||
class Server < Toro::Router
|
||||
private struct GraphQLData
|
||||
include JSON::Serializable
|
||||
|
||||
property query : String
|
||||
property variables : Hash(String, JSON::Any)?
|
||||
property operation_name : String?
|
||||
end
|
||||
|
||||
def routes
|
||||
on "graphql" do
|
||||
post do
|
||||
content_type "application/json"
|
||||
|
||||
data = GraphQLData.from_json(context.request.body.not_nil!.gets.not_nil!)
|
||||
|
||||
write Schema::SCHEMA.execute(
|
||||
data.query,
|
||||
data.variables,
|
||||
data.operation_name,
|
||||
Context.new(context.request)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
5
docker/backend/src/mw/version.cr
Normal file
5
docker/backend/src/mw/version.cr
Normal file
|
@ -0,0 +1,5 @@
|
|||
require "shard"
|
||||
|
||||
module MW
|
||||
VERSION = Shard.version
|
||||
end
|
129
docker/backend_old/.dockerignore
Normal file
129
docker/backend_old/.dockerignore
Normal file
|
@ -0,0 +1,129 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
.prettierrc
|
127
docker/backend_old/.gitignore
vendored
Normal file
127
docker/backend_old/.gitignore
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
1
docker/backend_old/.prettierrc
Normal file
1
docker/backend_old/.prettierrc
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
23
docker/backend_old/Dockerfile
Normal file
23
docker/backend_old/Dockerfile
Normal file
|
@ -0,0 +1,23 @@
|
|||
FROM node:17-alpine3.12 as deps
|
||||
WORKDIR /app
|
||||
COPY ./package.json ./yarn.lock ./
|
||||
RUN yarn --frozen-lockfile
|
||||
|
||||
FROM node:17-alpine3.12 as builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/package.json /app/yarn.lock ./
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY ./prisma ./prisma
|
||||
RUN npx prisma generate
|
||||
COPY . .
|
||||
RUN yarn build
|
||||
RUN npm prune --production
|
||||
|
||||
FROM node:17-alpine3.12 as runner
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/package.json .
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/prisma ./prisma
|
||||
EXPOSE 8080
|
||||
CMD ["yarn", "start"]
|
28
docker/backend_old/package.json
Normal file
28
docker/backend_old/package.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "dist/index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "node --trace-warnings --enable-source-maps dist/index.js",
|
||||
"build": "rm -rf dist/ && tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.5",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@envelop/apollo-server-errors": "^1.2.1",
|
||||
"@envelop/graphql-jit": "^1.3.1",
|
||||
"@envelop/resource-limitations": "^0.4.1",
|
||||
"@prisma/client": "^3.7.0",
|
||||
"argon2": "^0.28.3",
|
||||
"graphql": "15",
|
||||
"graphql-scalars": "^1.14.1",
|
||||
"graphql-yoga": "^2.0.0-alpha.6",
|
||||
"nexus": "^1.1.0",
|
||||
"nexus-prisma": "^0.35.0",
|
||||
"prisma": "^3.7.0"
|
||||
}
|
||||
}
|
47
docker/backend_old/prisma/schema.prisma
Normal file
47
docker/backend_old/prisma/schema.prisma
Normal file
|
@ -0,0 +1,47 @@
|
|||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
generator nexusPrisma {
|
||||
provider = "nexus-prisma"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("BACKEND_DATABASE_URL")
|
||||
}
|
||||
|
||||
enum Role {
|
||||
SUPER_ADMIN
|
||||
ADMIN
|
||||
TEACHER
|
||||
STUDENT
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
firstname String
|
||||
lastname String
|
||||
email String @unique
|
||||
password String
|
||||
blocked Boolean @default(false)
|
||||
role Role
|
||||
teacher Teacher?
|
||||
student Student?
|
||||
}
|
||||
|
||||
model Teacher {
|
||||
id String @id @default(uuid())
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId String @unique
|
||||
maxStudents Int
|
||||
}
|
||||
|
||||
model Student {
|
||||
id String @id @default(uuid())
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId String @unique
|
||||
}
|
7
docker/backend_old/src/context.ts
Normal file
7
docker/backend_old/src/context.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const client = new PrismaClient();
|
||||
|
||||
export class Context {
|
||||
constructor(public readonly req: unknown, public readonly prisma = client) {}
|
||||
}
|
34
docker/backend_old/src/index.ts
Normal file
34
docker/backend_old/src/index.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { createServer, useMaskedErrors } from "graphql-yoga";
|
||||
import { useGraphQlJit } from "@envelop/graphql-jit";
|
||||
import { useResourceLimitations } from "@envelop/resource-limitations";
|
||||
import { useApolloServerErrors } from "@envelop/apollo-server-errors";
|
||||
|
||||
import { schema } from "./schema";
|
||||
import { Context } from "./context";
|
||||
|
||||
const isProduction = process.env.NODE_ENV !== "development";
|
||||
|
||||
console.log(`NODE_ENV: ${process.env.NODE_ENV}`);
|
||||
console.log("Starting server...");
|
||||
|
||||
const server = createServer({
|
||||
endpoint: "/",
|
||||
port: 8080,
|
||||
graphiql: false,
|
||||
schema: schema as any,
|
||||
async context(req) {
|
||||
return new Context(req);
|
||||
},
|
||||
plugins: [
|
||||
useGraphQlJit(),
|
||||
useResourceLimitations({
|
||||
extensions: true,
|
||||
}),
|
||||
useMaskedErrors(),
|
||||
useApolloServerErrors({
|
||||
debug: !isProduction,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
server.start(() => console.log("Server is running on http://localhost:8080"));
|
10
docker/backend_old/src/schema/index.ts
Normal file
10
docker/backend_old/src/schema/index.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { makeSchema } from "nexus";
|
||||
|
||||
import { Query } from "./query";
|
||||
import { Mutation } from "./mutation";
|
||||
import { User, UserCreateInput } from "./user";
|
||||
import { Role } from "./role";
|
||||
|
||||
export const schema = makeSchema({
|
||||
types: [Query, Mutation, User, UserCreateInput, Role],
|
||||
});
|
27
docker/backend_old/src/schema/mutation.ts
Normal file
27
docker/backend_old/src/schema/mutation.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { mutationType, arg, nonNull } from "nexus";
|
||||
import * as argon2 from "argon2";
|
||||
|
||||
import { Context } from "../context";
|
||||
import { User, UserCreateInput } from "./user";
|
||||
|
||||
export const Mutation = mutationType({
|
||||
definition(t) {
|
||||
t.nonNull.field("createUser", {
|
||||
type: User,
|
||||
args: {
|
||||
input: nonNull(arg({ type: UserCreateInput })),
|
||||
},
|
||||
async resolve(_root, args, ctx: Context) {
|
||||
return ctx.prisma.user.create({
|
||||
data: {
|
||||
firstname: args.input.firstname,
|
||||
lastname: args.input.lastname,
|
||||
email: args.input.email,
|
||||
password: await argon2.hash(args.input.password),
|
||||
role: args.input.role,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
33
docker/backend_old/src/schema/query.ts
Normal file
33
docker/backend_old/src/schema/query.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { queryType, idArg, nonNull } from "nexus";
|
||||
|
||||
import { Context } from "../context";
|
||||
import { User } from "./user";
|
||||
|
||||
export const Query = queryType({
|
||||
definition(t) {
|
||||
t.nonNull.boolean("ok", {
|
||||
resolve() {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
t.field("user", {
|
||||
type: User,
|
||||
args: {
|
||||
id: nonNull(idArg()),
|
||||
},
|
||||
resolve(_root, args, ctx: Context) {
|
||||
return ctx.prisma.user.findUnique({
|
||||
where: {
|
||||
id: args.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
t.nonNull.list.nonNull.field("users", {
|
||||
type: User,
|
||||
resolve(_root, _args, ctx: Context) {
|
||||
return ctx.prisma.user.findMany();
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
7
docker/backend_old/src/schema/role.ts
Normal file
7
docker/backend_old/src/schema/role.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { enumType } from "nexus";
|
||||
import * as nPrisma from "nexus-prisma";
|
||||
|
||||
export const Role = enumType({
|
||||
name: nPrisma.Role.name,
|
||||
members: nPrisma.Role.members,
|
||||
});
|
28
docker/backend_old/src/schema/user.ts
Normal file
28
docker/backend_old/src/schema/user.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { objectType, inputObjectType } from "nexus";
|
||||
import * as nPrisma from "nexus-prisma";
|
||||
|
||||
import { Role } from "./role";
|
||||
|
||||
export const User = objectType({
|
||||
name: nPrisma.User.$name,
|
||||
definition(t) {
|
||||
t.field(nPrisma.User.id);
|
||||
t.field(nPrisma.User.firstname);
|
||||
t.field(nPrisma.User.lastname);
|
||||
t.field(nPrisma.User.email);
|
||||
t.field(nPrisma.User.role);
|
||||
t.field(nPrisma.User.blocked);
|
||||
},
|
||||
});
|
||||
|
||||
export const UserCreateInput = inputObjectType({
|
||||
name: "UserCreateInput",
|
||||
definition(t) {
|
||||
t.nonNull.string("firstname");
|
||||
t.nonNull.string("lastname");
|
||||
t.nonNull.string("email");
|
||||
t.nonNull.string("password");
|
||||
t.nonNull.field("role", { type: Role });
|
||||
t.boolean("blocked", { default: false });
|
||||
},
|
||||
});
|
103
docker/backend_old/tsconfig.json
Normal file
103
docker/backend_old/tsconfig.json
Normal file
|
@ -0,0 +1,103 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
"lib": [
|
||||
"ESNext"
|
||||
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
|
||||
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs" /* Specify what module code is generated. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */,
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files */
|
||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||
|
||||
/* Emit */
|
||||
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */,
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
|
||||
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
1380
docker/backend_old/yarn.lock
Normal file
1380
docker/backend_old/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
3
scripts/micrate.sh
Normal file
3
scripts/micrate.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
docker-compose exec backend /micrate "$@"
|
Loading…
Reference in a new issue