275 lines
6.2 KiB
Crystal
275 lines
6.2 KiB
Crystal
require "kemal"
|
||
require "uuid"
|
||
require "file_utils"
|
||
|
||
require "authd"
|
||
|
||
require "./project.cr"
|
||
require "./requests.cr"
|
||
|
||
authd_key = nil : String?
|
||
storage_directory = "./projects"
|
||
|
||
# It’ll get replaced later. But FIXME, this is only a temporary workaround.
|
||
authd = nil.unsafe_as AuthD::Client
|
||
|
||
Kemal.config.extra_options do |parser|
|
||
parser.on "-k file", "--jwt-key file", "Provides the JWT key for authd." do |file|
|
||
authd_key = File.read(file).chomp
|
||
end
|
||
|
||
parser.on "-S dir", "--storage dir", "Provides a directory in which to store kanband’s data and projects." do |dir|
|
||
storage_directory = dir
|
||
end
|
||
end
|
||
|
||
sockets = [] of HTTP::WebSocket
|
||
users = {} of HTTP::WebSocket => AuthD::User
|
||
|
||
get "/" do
|
||
File.read "index.html"
|
||
end
|
||
|
||
get "/style.css" do |env|
|
||
env.response.content_type = "text/css"
|
||
File.read "style.css"
|
||
end
|
||
|
||
get "/main.js" do |env|
|
||
env.response.content_type = "text/javascript"
|
||
File.read "main.js"
|
||
end
|
||
|
||
ws "/socket" do |socket|
|
||
socket.on_close do
|
||
users.delete socket
|
||
sockets.delete socket
|
||
end
|
||
|
||
socket.on_message do |message_as_s|
|
||
message = JSON.parse(message_as_s).as_h
|
||
|
||
case message["type"]
|
||
when "login"
|
||
request = Requests::Login.from_json message_as_s
|
||
|
||
login = request.login
|
||
password = request.password
|
||
|
||
user = authd.get_user? login, password
|
||
|
||
unless user
|
||
socket.send({
|
||
type: "login-error",
|
||
error: "Invalid credentials."
|
||
}.to_json)
|
||
|
||
next
|
||
end
|
||
|
||
users[socket] = user
|
||
sockets << socket
|
||
|
||
socket.send({
|
||
type: "login"
|
||
}.to_json)
|
||
|
||
socket.send({
|
||
type: "list-projects",
|
||
projects: Project.all storage_directory
|
||
}.to_json)
|
||
when "new-project"
|
||
user = users[socket] # FIXME: make it an authentication error
|
||
|
||
request = Requests::NewProject.from_json message_as_s
|
||
|
||
project = Project.new request.name
|
||
|
||
project.write! storage_directory
|
||
|
||
# FIXME: Only notify concerned users.
|
||
sockets.each &.send({
|
||
type: "list-projects",
|
||
projects: Project.all storage_directory
|
||
}.to_json)
|
||
|
||
sockets.each &.send({
|
||
type: "project",
|
||
project: project
|
||
}.to_json)
|
||
when "project"
|
||
user = users[socket] # FIXME: make it an authentication error
|
||
|
||
request = Requests::Tasks.from_json message_as_s
|
||
|
||
project = Project.get_from_id request.project, storage_directory
|
||
|
||
socket.send({
|
||
type: "project",
|
||
project: project
|
||
}.to_json)
|
||
when "edit-project"
|
||
user = users[socket] # FIXME: make it an authentication error
|
||
|
||
request = Requests::EditProject.from_json message_as_s
|
||
|
||
project = Project.get_from_id request.project, storage_directory
|
||
|
||
if name = request.name
|
||
project.name = name
|
||
end
|
||
|
||
if color = request.color
|
||
project.color = color
|
||
end
|
||
|
||
project.write! storage_directory
|
||
|
||
# FIXME: Only notify concerned users.
|
||
sockets.each &.send({
|
||
type: "project",
|
||
project: project
|
||
}.to_json)
|
||
when "new-column"
|
||
user = users[socket] # FIXME: make it an authentication error
|
||
|
||
request = Requests::NewColumn.from_json message_as_s
|
||
|
||
project = Project.get_from_id request.project, storage_directory
|
||
|
||
project.columns << Column.new request.name
|
||
project.write! storage_directory
|
||
|
||
# FIXME: Only notify concerned users.
|
||
sockets.each &.send({
|
||
type: "project",
|
||
project: project
|
||
}.to_json)
|
||
when "new-task"
|
||
user = users[socket] # FIXME: make it an authentication error
|
||
|
||
request = Requests::NewTask.from_json message_as_s
|
||
|
||
project = Project.get_from_id request.project, storage_directory
|
||
column = project.columns.find(&.id.==(request.column)).not_nil!
|
||
|
||
project.tasks << Task.new request.title, user.uid, request.description, column.id
|
||
|
||
project.write! storage_directory
|
||
|
||
# FIXME: Only notify concerned users.
|
||
sockets.each &.send({
|
||
type: "project",
|
||
project: project
|
||
}.to_json)
|
||
when "edit-task"
|
||
user = users[socket] # FIXME: make it an authentication error
|
||
|
||
request = Requests::EditTask.from_json message_as_s
|
||
|
||
project = Project.get_from_id request.project, storage_directory
|
||
task = project.tasks.find(&.id.==(request.task)).not_nil!
|
||
|
||
if column = request.column
|
||
column = project.columns.find(&.id.==(column)).not_nil!
|
||
|
||
task.column = column.id
|
||
end
|
||
|
||
if title = request.title
|
||
task.title = title
|
||
end
|
||
|
||
if description = request.description
|
||
task.description = description
|
||
end
|
||
|
||
# FIXME: Check it’s a valid UID.
|
||
if assigned_to = request.assigned_to
|
||
# FIXME: Probably not the best way to handle this corner-case.
|
||
assigned_to = nil if assigned_to == -1
|
||
|
||
task.assigned_to = assigned_to
|
||
end
|
||
|
||
project.write! storage_directory
|
||
|
||
# FIXME: Only notify concerned users.
|
||
sockets.each &.send({
|
||
type: "project",
|
||
project: project
|
||
}.to_json)
|
||
when "delete-task"
|
||
user = users[socket]
|
||
|
||
request = Requests::DeleteTask.from_json message_as_s
|
||
|
||
project = Project.get_from_id request.project, storage_directory
|
||
|
||
project.tasks.select! &.id.!=(request.task)
|
||
|
||
project.write! storage_directory
|
||
|
||
# FIXME: Only notify concerned users.
|
||
sockets.each &.send({
|
||
type: "project",
|
||
project: project
|
||
}.to_json)
|
||
when "edit-column"
|
||
user = users[socket] # FIXME: make it an authentication error
|
||
|
||
request = Requests::EditColumn.from_json message_as_s
|
||
|
||
project = Project.get_from_id request.project, storage_directory
|
||
column = project.columns.find(&.id.==(request.column)).not_nil!
|
||
|
||
if name = request.name
|
||
column.name = name
|
||
end
|
||
|
||
puts project.columns.to_json
|
||
|
||
project.write! storage_directory
|
||
|
||
# FIXME: Only notify concerned users.
|
||
sockets.each &.send({
|
||
type: "project",
|
||
project: project
|
||
}.to_json)
|
||
when "delete-column"
|
||
user = users[socket]
|
||
|
||
request = Requests::DeleteColumn.from_json message_as_s
|
||
project = Project.get_from_id request.project, storage_directory
|
||
|
||
project.columns.select! &.id.!=(request.column)
|
||
project.tasks.select! &.column.!=(request.column)
|
||
|
||
project.write! storage_directory
|
||
|
||
# FIXME: Only notify concerned users.
|
||
sockets.each &.send({
|
||
type: "project",
|
||
project: project
|
||
}.to_json)
|
||
when "get-user"
|
||
request = Requests::GetUser.from_json message_as_s
|
||
|
||
user = authd.get_user? request.uid
|
||
|
||
socket.send({
|
||
type: "user",
|
||
user: user
|
||
}.to_json)
|
||
end
|
||
end
|
||
end
|
||
|
||
Kemal.run do
|
||
authd = AuthD::Client.new
|
||
|
||
key = authd_key
|
||
authd.key = key if key.is_a? String
|
||
end
|
||
|