todod/src/main.cr

323 lines
7.3 KiB
Crystal
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

require "uuid"
require "file_utils"
require "option_parser"
require "authd"
require "./project.cr"
require "./requests.cr"
authd_key = nil : String?
storage_directory = "./projects"
# Itll get replaced later. But FIXME, this is only a temporary workaround.
authd = nil.unsafe_as AuthD::Client
OptionParser.parse 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 kanbands data and projects." do |dir|
storage_directory = dir
end
parser.on "-h", "--help", "Prints this help message." do
puts parser
exit 0
end
end
class ConnectionArray < Array(Int32)
def <<(x : IPC::Connection)
self << x.fd
end
def [](key) : IPC::Connection
fd = fetch key
connection = LibIPC::Connection.new# 0, 0, fd, 0, Pointer(UInt8).null
connection.fd = fd
IPC::Connection.new connection
end
def each
super do |fd|
connection = LibIPC::Connection.new# 0, 0, fd, 0, Pointer(UInt8).null
connection.fd = fd
yield IPC::Connection.new connection
end
end
end
class ConnectionHash < Hash(Int32, AuthD::User)
def [](key : IPC::Connection)
self[key.fd]
end
end
sockets = ConnectionArray.new
users = ConnectionHash.new
authd = AuthD::Client.new
key = authd_key
authd.key = key if key.is_a? String
# FIXME: Upstream this?
class IPC::Service
def loop(&block : Proc(IPC::Event::Connection | IPC::Event::Disconnection | IPC::Event::Message | IPC::Exception, Nil))
previous_def do |event|
begin
block.call event
rescue e
STDERR.puts "IPC::Service caught an exception: #{e}"
end
end
end
end
IPC::Service.new "kanban" do |event|
case event
when IPC::Event::Connection
when IPC::Event::Disconnection
socket = event.connection
users.delete socket
sockets.delete socket
when IPC::Event::Message
message_as_s = String.new event.message.payload
begin
message = JSON.parse(message_as_s).as_h
rescue
next # Dropping malformed requests.
end
connection = event.connection
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
connection.send(0, {
type: "login-error",
error: "Invalid credentials."
}.to_json)
next
end
users[connection.fd] = user
sockets << connection.fd
connection.send(0, {
type: "login"
}.to_json)
connection.send(0, {
type: "list-projects",
projects: Project.all storage_directory
}.to_json)
when "new-project"
user = users[connection] # 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(0, {
type: "list-projects",
projects: Project.all storage_directory
}.to_json)
sockets.each &.send(0, {
type: "project",
project: project
}.to_json)
when "project"
user = users[connection] # FIXME: make it an authentication error
request = Requests::Tasks.from_json message_as_s
project = Project.get_from_id request.project, storage_directory
connection.send(0, {
type: "project",
project: project
}.to_json)
when "edit-project"
user = users[connection] # 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(0, {
type: "project",
project: project
}.to_json)
when "new-column"
user = users[connection] # 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(0, {
type: "project",
project: project
}.to_json)
when "new-task"
user = users[connection] # 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(0, {
type: "project",
project: project
}.to_json)
when "edit-task"
user = users[connection] # 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
if color = request.color
task.color = color
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! storage_directory
# FIXME: Only notify concerned users.
sockets.each &.send(0, {
type: "project",
project: project
}.to_json)
when "delete-task"
user = users[connection]
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(0, {
type: "project",
project: project
}.to_json)
when "edit-column"
user = users[connection] # 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(0, {
type: "project",
project: project
}.to_json)
when "delete-column"
user = users[connection]
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(0, {
type: "project",
project: project
}.to_json)
when "get-user"
request = Requests::GetUser.from_json message_as_s
user = authd.get_user? request.uid
connection.send(0, {
type: "user",
user: user
}.to_json)
else
raise "Unknown request type: '#{message["type"]}'"
end
else
pp event
end
end