Grooming, new CLI options.
parent
802812e99f
commit
f0295031e9
|
@ -0,0 +1,13 @@
|
|||
require "uuid"
|
||||
|
||||
class Column
|
||||
JSON.mapping({
|
||||
id: String,
|
||||
name: String
|
||||
})
|
||||
|
||||
def initialize(@name)
|
||||
@id = UUID.random.to_s
|
||||
end
|
||||
end
|
||||
|
233
src/main.cr
233
src/main.cr
|
@ -4,186 +4,40 @@ require "file_utils"
|
|||
|
||||
require "authd"
|
||||
|
||||
class Task
|
||||
JSON.mapping({
|
||||
id: String,
|
||||
author: Int32,
|
||||
title: String,
|
||||
description: String,
|
||||
column: String,
|
||||
assigned_to: Int32?
|
||||
})
|
||||
require "./project.cr"
|
||||
require "./requests.cr"
|
||||
|
||||
def initialize(@title, @author, @description, @column)
|
||||
@id = UUID.random.to_s
|
||||
end
|
||||
end
|
||||
authd_key = nil : String?
|
||||
storage_directory = "./projects"
|
||||
|
||||
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
|
||||
# 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
|
||||
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
|
||||
File.read "style.css"
|
||||
end
|
||||
|
||||
get "/main.js" do
|
||||
File.read "main.js"
|
||||
end
|
||||
|
||||
ws "/socket" do |socket|
|
||||
socket.on_close do
|
||||
users.delete socket
|
||||
|
@ -220,7 +74,7 @@ ws "/socket" do |socket|
|
|||
|
||||
socket.send({
|
||||
type: "list-projects",
|
||||
projects: Project.all
|
||||
projects: Project.all storage_directory
|
||||
}.to_json)
|
||||
when "new-project"
|
||||
user = users[socket] # FIXME: make it an authentication error
|
||||
|
@ -229,6 +83,8 @@ ws "/socket" do |socket|
|
|||
|
||||
project = Project.new request.name
|
||||
|
||||
project.write! storage_directory
|
||||
|
||||
# FIXME: Only notify concerned users.
|
||||
sockets.each &.send({
|
||||
type: "project",
|
||||
|
@ -239,7 +95,7 @@ ws "/socket" do |socket|
|
|||
|
||||
request = Requests::Tasks.from_json message_as_s
|
||||
|
||||
project = Project.get_from_id request.project
|
||||
project = Project.get_from_id request.project, storage_directory
|
||||
|
||||
socket.send({
|
||||
type: "project",
|
||||
|
@ -250,7 +106,7 @@ ws "/socket" do |socket|
|
|||
|
||||
request = Requests::EditProject.from_json message_as_s
|
||||
|
||||
project = Project.get_from_id request.project
|
||||
project = Project.get_from_id request.project, storage_directory
|
||||
|
||||
if name = request.name
|
||||
project.name = name
|
||||
|
@ -260,7 +116,7 @@ ws "/socket" do |socket|
|
|||
project.color = color
|
||||
end
|
||||
|
||||
project.write!
|
||||
project.write! storage_directory
|
||||
|
||||
# FIXME: Only notify concerned users.
|
||||
sockets.each &.send({
|
||||
|
@ -272,10 +128,10 @@ ws "/socket" do |socket|
|
|||
|
||||
request = Requests::NewColumn.from_json message_as_s
|
||||
|
||||
project = Project.get_from_id request.project
|
||||
project = Project.get_from_id request.project, storage_directory
|
||||
|
||||
project.columns << Column.new request.name
|
||||
project.write!
|
||||
project.write! storage_directory
|
||||
|
||||
# FIXME: Only notify concerned users.
|
||||
sockets.each &.send({
|
||||
|
@ -287,12 +143,12 @@ ws "/socket" do |socket|
|
|||
|
||||
request = Requests::NewTask.from_json message_as_s
|
||||
|
||||
project = Project.get_from_id request.project
|
||||
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!
|
||||
project.write! storage_directory
|
||||
|
||||
# FIXME: Only notify concerned users.
|
||||
sockets.each &.send({
|
||||
|
@ -304,7 +160,7 @@ ws "/socket" do |socket|
|
|||
|
||||
request = Requests::EditTask.from_json message_as_s
|
||||
|
||||
project = Project.get_from_id request.project
|
||||
project = Project.get_from_id request.project, storage_directory
|
||||
task = project.tasks.find(&.id.==(request.task)).not_nil!
|
||||
|
||||
if column = request.column
|
||||
|
@ -329,7 +185,7 @@ ws "/socket" do |socket|
|
|||
task.assigned_to = assigned_to
|
||||
end
|
||||
|
||||
project.write!
|
||||
project.write! storage_directory
|
||||
|
||||
# FIXME: Only notify concerned users.
|
||||
sockets.each &.send({
|
||||
|
@ -341,11 +197,11 @@ ws "/socket" do |socket|
|
|||
|
||||
request = Requests::DeleteTask.from_json message_as_s
|
||||
|
||||
project = Project.get_from_id request.project
|
||||
project = Project.get_from_id request.project, storage_directory
|
||||
|
||||
project.tasks.select! &.id.!=(request.task)
|
||||
|
||||
project.write!
|
||||
project.write! storage_directory
|
||||
|
||||
# FIXME: Only notify concerned users.
|
||||
sockets.each &.send({
|
||||
|
@ -357,7 +213,7 @@ ws "/socket" do |socket|
|
|||
|
||||
request = Requests::EditColumn.from_json message_as_s
|
||||
|
||||
project = Project.get_from_id request.project
|
||||
project = Project.get_from_id request.project, storage_directory
|
||||
column = project.columns.find(&.id.==(request.column)).not_nil!
|
||||
|
||||
if name = request.name
|
||||
|
@ -366,7 +222,7 @@ ws "/socket" do |socket|
|
|||
|
||||
puts project.columns.to_json
|
||||
|
||||
project.write!
|
||||
project.write! storage_directory
|
||||
|
||||
# FIXME: Only notify concerned users.
|
||||
sockets.each &.send({
|
||||
|
@ -377,12 +233,12 @@ ws "/socket" do |socket|
|
|||
user = users[socket]
|
||||
|
||||
request = Requests::DeleteColumn.from_json message_as_s
|
||||
project = Project.get_from_id request.project
|
||||
project = Project.get_from_id request.project, storage_directory
|
||||
|
||||
project.columns.select! &.id.!=(request.column)
|
||||
project.tasks.select! &.column.!=(request.column)
|
||||
|
||||
project.write!
|
||||
project.write! storage_directory
|
||||
|
||||
# FIXME: Only notify concerned users.
|
||||
sockets.each &.send({
|
||||
|
@ -402,5 +258,10 @@ ws "/socket" do |socket|
|
|||
end
|
||||
end
|
||||
|
||||
Kemal.run
|
||||
Kemal.run do
|
||||
authd = AuthD::Client.new
|
||||
|
||||
key = authd_key
|
||||
authd.key = key if key.is_a? String
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
require "uuid"
|
||||
require "file_utils"
|
||||
|
||||
require "./task.cr"
|
||||
require "./column.cr"
|
||||
|
||||
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"
|
||||
end
|
||||
|
||||
def write!(storage)
|
||||
Dir.mkdir_p "#{storage}/#{@id}"
|
||||
|
||||
File.write "#{storage}/#{@id}/project.json.new", to_json
|
||||
FileUtils.mv "#{storage}/#{@id}/project.json.new",
|
||||
"#{storage}/#{@id}/project.json"
|
||||
end
|
||||
|
||||
# FIXME: Transform that exception somehow.
|
||||
# FIXME: Update to not read everything.
|
||||
def self.get_from_id(id, storage)
|
||||
self.all(storage).find(&.id.==(id)).not_nil!
|
||||
end
|
||||
|
||||
def self.all(storage) : Array(Project)
|
||||
# FIXME: switch to an index file per project in a storage directory.
|
||||
Dir.children(storage).compact_map do |filename|
|
||||
begin
|
||||
Project.from_json File.read "#{storage}/#{filename}/project.json"
|
||||
rescue
|
||||
end
|
||||
end
|
||||
rescue
|
||||
[] of Project
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
require "json"
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
require "uuid"
|
||||
|
||||
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
|
||||
|
Loading…
Reference in New Issue