todod/src/main.cr

407 lines
7.6 KiB
Crystal
Raw Normal View History

2019-07-06 02:35:50 +02:00
require "kemal"
require "uuid"
require "file_utils"
require "authd"
class Task
JSON.mapping({
id: String,
author: Int32,
title: String,
description: String,
column: String,
assigned_to: Int32?
})
def initialize(@title, @author, @description, @column)
@id = UUID.random.to_s
end
end
class Column
JSON.mapping({
id: String,
name: String
})
def initialize(@name)
@id = UUID.random.to_s
end
end
class Project
JSON.mapping({
id: String,
name: String,
columns: Array(Column),
tasks: Array(Task),
color: {
type: String,
default: "dark"
}
})
def initialize(@name)
@id = UUID.random.to_s
@columns = [Column.new "To do"]
@tasks = [] of Task
@color = "dark"
write!
end
def write!
Dir.mkdir_p "./projects/#{@id}"
File.write "./projects/#{@id}/project.json.new", to_json
FileUtils.mv "./projects/#{@id}/project.json.new",
"./projects/#{@id}/project.json"
end
# FIXME: Transform that exception somehow.
# FIXME: Update to not read everything.
def self.get_from_id(id)
self.all.find(&.id.==(id)).not_nil!
end
def self.all : Array(Project)
# FIXME: switch to an index file per project in a storage directory.
Dir.children("./projects").compact_map do |filename|
begin
Project.from_json File.read "./projects/#{filename}/project.json"
rescue
end
end
rescue
[] of Project
end
end
class Requests::Login
JSON.mapping({
type: String,
login: String,
password: String
})
end
class Requests::Tasks
JSON.mapping({
type: String,
project: String
})
end
class Requests::NewProject
JSON.mapping({
type: String,
name: String
})
end
class Requests::EditProject
JSON.mapping({
type: String,
project: String,
name: String?,
color: String?
})
end
class Requests::NewColumn
JSON.mapping({
type: String,
project: String,
name: String
})
end
class Requests::NewTask
JSON.mapping({
type: String,
project: String,
column: String,
title: String,
description: String
})
end
class Requests::EditTask
JSON.mapping({
type: String,
project: String,
task: String,
column: String?,
title: String?,
description: String?,
assigned_to: Int32?
})
end
class Requests::DeleteTask
JSON.mapping({
type: String,
project: String,
task: String
})
end
class Requests::EditColumn
JSON.mapping({
type: String,
project: String,
column: String,
name: String?
})
end
class Requests::DeleteColumn
JSON.mapping({
type: String,
project: String,
column: String
})
end
class Requests::GetUser
JSON.mapping({
type: String,
uid: Int32
})
end
authd = AuthD::Client.new
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
end
sockets = [] of HTTP::WebSocket
users = {} of HTTP::WebSocket => AuthD::User
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
}.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
# FIXME: Only notify concerned users.
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
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
if name = request.name
project.name = name
end
if color = request.color
project.color = color
end
project.write!
# 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
project.columns << Column.new request.name
project.write!
# 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
column = project.columns.find(&.id.==(request.column)).not_nil!
project.tasks << Task.new request.title, user.uid, request.description, column.id
project.write!
# 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
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 its 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!
# 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
project.tasks.select! &.id.!=(request.task)
project.write!
# 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
column = project.columns.find(&.id.==(request.column)).not_nil!
if name = request.name
column.name = name
end
puts project.columns.to_json
project.write!
# 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
project.columns.select! &.id.!=(request.column)
project.tasks.select! &.column.!=(request.column)
project.write!
# 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