unitylock 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.babelrc +8 -0
- data/.gitignore +11 -0
- data/.node-version +1 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +53 -0
- data/LICENSE.txt +21 -0
- data/Procfile +1 -0
- data/README.md +32 -0
- data/Rakefile +10 -0
- data/app.json +7 -0
- data/app/action_creators/Lock.js +22 -0
- data/app/action_creators/Login.js +10 -0
- data/app/action_creators/LoginDialogAction.js +9 -0
- data/app/action_creators/Search.js +13 -0
- data/app/action_creators/SnackMessage.js +6 -0
- data/app/action_creators/Unlock.js +22 -0
- data/app/client.js +1 -0
- data/app/components/AppSnackBarComponent.js +24 -0
- data/app/components/FooterComponent.js +40 -0
- data/app/components/LockAppBarComponent.js +60 -0
- data/app/components/LockTableComponent.js +59 -0
- data/app/components/LoginDialogComponent.js +69 -0
- data/app/containers/App.js +18 -0
- data/app/containers/AppSnackBar.js +19 -0
- data/app/containers/LockAppBar.js +30 -0
- data/app/containers/LockTable.js +44 -0
- data/app/containers/LoginDialog.js +32 -0
- data/app/index.html +20 -0
- data/app/index.js +44 -0
- data/app/reducers/index.js +33 -0
- data/app/server.js +2 -0
- data/app/src/client.js +35 -0
- data/app/src/helper.js +85 -0
- data/app/src/row.js +60 -0
- data/app/src/socket.js +47 -0
- data/app/test/helper.spec.js +47 -0
- data/app/utils/FileUtils.js +11 -0
- data/art/unitylock-logo.png +0 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config.ru +4 -0
- data/config/puma.rb +9 -0
- data/exe/unitylock +44 -0
- data/lib/unitylock/client.rb +2 -0
- data/lib/unitylock/client/main.rb +90 -0
- data/lib/unitylock/server.rb +2 -0
- data/lib/unitylock/server/entity/unityfile.rb +38 -0
- data/lib/unitylock/server/model.rb +59 -0
- data/lib/unitylock/server/router.rb +71 -0
- data/lib/unitylock/server/service.rb +41 -0
- data/lib/unitylock/version.rb +3 -0
- data/package.json +52 -0
- data/public/favicon.ico +0 -0
- data/public/static/bundle.js +98 -0
- data/public/static/main.css +82 -0
- data/unitylock.gemspec +25 -0
- data/webpack.config.js +40 -0
- metadata +148 -0
data/app/src/row.js
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
class Row {
|
2
|
+
constructor(path, helper) {
|
3
|
+
this.path = path
|
4
|
+
this.helper = helper
|
5
|
+
}
|
6
|
+
|
7
|
+
// TODO: sync
|
8
|
+
lock (user) {
|
9
|
+
return new Promise((resolve, reject) => {
|
10
|
+
helper.fetch_by_path(this.path)
|
11
|
+
.then(row => {
|
12
|
+
if (row === undefined) {
|
13
|
+
reject("Not found lock object.")
|
14
|
+
return
|
15
|
+
}
|
16
|
+
|
17
|
+
if (row.owner != null) {
|
18
|
+
reject("Owner already exist.")
|
19
|
+
return
|
20
|
+
}
|
21
|
+
|
22
|
+
this.owner = user
|
23
|
+
helper
|
24
|
+
.create_or_update_rows([this])
|
25
|
+
.then(row => resolve(this))
|
26
|
+
.catch(err => reject(err))
|
27
|
+
})
|
28
|
+
})
|
29
|
+
}
|
30
|
+
|
31
|
+
// TODO: sync
|
32
|
+
unlock (user) {
|
33
|
+
var row = helper.fetch_by_path(this.path)
|
34
|
+
if (row === undefined) {
|
35
|
+
return false
|
36
|
+
}
|
37
|
+
|
38
|
+
if (row.owner != user) {
|
39
|
+
return false
|
40
|
+
}
|
41
|
+
|
42
|
+
this.owner = null
|
43
|
+
helper.create_or_update_rows([this])
|
44
|
+
return true
|
45
|
+
}
|
46
|
+
|
47
|
+
delete () {
|
48
|
+
helper.delete_by_path(this.path)
|
49
|
+
}
|
50
|
+
|
51
|
+
static touches(helper, pathes) {
|
52
|
+
helper.create_or_update_rows(pathes)
|
53
|
+
}
|
54
|
+
|
55
|
+
static list(helper) {
|
56
|
+
helper.search()
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
export default Row
|
data/app/src/socket.js
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
var port = process.env.WEBSOCKET_PORT || 8080;
|
2
|
+
var WebSocketServer = require('websocket').server;
|
3
|
+
var http = require('http');
|
4
|
+
var server = http.createServer(function(request, response) {
|
5
|
+
console.log((new Date()) + ' Received request for ' + request.url);
|
6
|
+
response.writeHead(404);
|
7
|
+
response.end();
|
8
|
+
});
|
9
|
+
|
10
|
+
server.listen(port, function() {
|
11
|
+
console.log((new Date()) + ' WebSocket port is ' + port);
|
12
|
+
});
|
13
|
+
|
14
|
+
var wsServer = new WebSocketServer({
|
15
|
+
httpServer: server,
|
16
|
+
autoAcceptConnections: false
|
17
|
+
});
|
18
|
+
|
19
|
+
function originIsAllowed(origin) {
|
20
|
+
return true;
|
21
|
+
}
|
22
|
+
|
23
|
+
wsServer.on('request', function(request) {
|
24
|
+
if (!originIsAllowed(request.origin)) {
|
25
|
+
// Make sure we only accept requests from an allowed origin
|
26
|
+
request.reject();
|
27
|
+
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
|
28
|
+
return;
|
29
|
+
}
|
30
|
+
|
31
|
+
var connection = request.accept('echo-protocol', request.origin);
|
32
|
+
console.log((new Date()) + ' Connection accepted.');
|
33
|
+
connection.on('message', function(message) {
|
34
|
+
if (message.type === 'utf8') {
|
35
|
+
console.log('Received Message: ' + message.utf8Data);
|
36
|
+
connection.sendUTF(message.utf8Data);
|
37
|
+
}
|
38
|
+
else if (message.type === 'binary') {
|
39
|
+
console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
|
40
|
+
connection.sendBytes(message.binaryData);
|
41
|
+
}
|
42
|
+
});
|
43
|
+
connection.on('close', function(reasonCode, description) {
|
44
|
+
console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
|
45
|
+
});
|
46
|
+
});
|
47
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import assert from 'assert'
|
2
|
+
import fs from 'fs'
|
3
|
+
import Helper from '../src/helper.js'
|
4
|
+
import Row from '../src/row.js'
|
5
|
+
let test_file = 'test.lock'
|
6
|
+
|
7
|
+
describe('Helper', () => {
|
8
|
+
beforeEach((done) => {
|
9
|
+
fs.unlink(test_file, (err) => {
|
10
|
+
done()
|
11
|
+
})
|
12
|
+
})
|
13
|
+
|
14
|
+
it('can instantiate', () => {
|
15
|
+
assert.ok(new Helper(test_file) != null, "Helper is not null")
|
16
|
+
})
|
17
|
+
|
18
|
+
it('can search when initialized', (done) => {
|
19
|
+
new Helper(test_file)
|
20
|
+
.search()
|
21
|
+
.then(rows => {
|
22
|
+
assert.ok(rows.length == 0, "length should be zero")
|
23
|
+
done()
|
24
|
+
})
|
25
|
+
.catch(err => {
|
26
|
+
assert.ok(err == null, "should not raise error")
|
27
|
+
done()
|
28
|
+
})
|
29
|
+
})
|
30
|
+
|
31
|
+
it('can create_or_update_by_pathes', (done) => {
|
32
|
+
new Helper(test_file)
|
33
|
+
.create_or_update_by_pathes(["key1", "key2"])
|
34
|
+
.then(rows => {
|
35
|
+
assert.equal(2, rows.length, "length should be two")
|
36
|
+
assert.equal("key1", rows[0].path, "path should not be null")
|
37
|
+
assert.ok(null != rows[0].updated_at, "updated_at should not be null")
|
38
|
+
assert.equal("key2", rows[1].path, "path should not be null")
|
39
|
+
assert.ok(null != rows[1].updated_at, "updated_at should not be null")
|
40
|
+
done()
|
41
|
+
})
|
42
|
+
.catch(err => {
|
43
|
+
assert.ok(err == null, "should not raise error")
|
44
|
+
done()
|
45
|
+
})
|
46
|
+
})
|
47
|
+
})
|
Binary file
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "unitylock/server"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/config.ru
ADDED
data/config/puma.rb
ADDED
data/exe/unitylock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'unitylock/client'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
PROJECT_CONFIG_FILE = '.unitylock'
|
6
|
+
|
7
|
+
params = ARGV.getopts('', 'user:', 'force')
|
8
|
+
command = ARGV.shift
|
9
|
+
project_config = File.exists?(PROJECT_CONFIG_FILE) ? JSON.parse(File.read(PROJECT_CONFIG_FILE), {symbolize_names: true}) : {}
|
10
|
+
url = project_config[:url] || 'http://localhost:3000'
|
11
|
+
client = Unitylock::Client::Main.new(url)
|
12
|
+
|
13
|
+
case command
|
14
|
+
when "search" then
|
15
|
+
puts client.search.body
|
16
|
+
when "touches" then
|
17
|
+
puts client.touches(ARGV).body
|
18
|
+
when "deletes" then
|
19
|
+
puts client.deletes(ARGV).body
|
20
|
+
when "delete_all" then
|
21
|
+
puts client.delete_all.body
|
22
|
+
when "lock" then
|
23
|
+
user = params['user']
|
24
|
+
raise "requires parameter 'file'" unless ARGV.size > 0
|
25
|
+
raise "requires parameter 'user'" unless user != nil
|
26
|
+
puts client.locks(user: user, files: ARGV)
|
27
|
+
when "unlock" then
|
28
|
+
user = params['user']
|
29
|
+
raise "requires parameter 'file'" unless ARGV.size > 0
|
30
|
+
raise "requires parameter 'user'" unless user != nil
|
31
|
+
puts client.unlocks(user: user, files: ARGV)
|
32
|
+
when 'init' then
|
33
|
+
print 'url (http://localhost:3000): '
|
34
|
+
url = gets.chomp.sub(/^$/, 'http://localhost:3000')
|
35
|
+
File.write(PROJECT_CONFIG_FILE, JSON.pretty_generate({url: url}))
|
36
|
+
puts 'Created .unitylock'
|
37
|
+
when 'sync' then
|
38
|
+
force = params['force'] || false
|
39
|
+
client.sync(dir: '.', force: force)
|
40
|
+
else
|
41
|
+
puts "undefined command"
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'json'
|
3
|
+
require 'find'
|
4
|
+
|
5
|
+
module Unitylock
|
6
|
+
module Client
|
7
|
+
class Main
|
8
|
+
def initialize(urlbase)
|
9
|
+
@conn = Faraday.new(:url => urlbase) do |faraday|
|
10
|
+
faraday.request :url_encoded
|
11
|
+
# faraday.response :logger
|
12
|
+
faraday.adapter Faraday.default_adapter
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def search
|
17
|
+
@conn.get '/files'
|
18
|
+
end
|
19
|
+
|
20
|
+
def touches(files)
|
21
|
+
@conn.post do |req|
|
22
|
+
req.url '/files'
|
23
|
+
req.headers['Content-Type'] = 'application/json'
|
24
|
+
req.body = JSON.generate(files)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def deletes(files)
|
29
|
+
@conn.delete do |req|
|
30
|
+
req.url '/files'
|
31
|
+
req.headers['Content-Type'] = 'application/json'
|
32
|
+
req.body = JSON.generate(files)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete_all
|
37
|
+
files = JSON.parse(search.body).collect {|it| it['file']}
|
38
|
+
deletes(files)
|
39
|
+
end
|
40
|
+
|
41
|
+
def locks(user:, files:)
|
42
|
+
# TODO: requests at once
|
43
|
+
results = []
|
44
|
+
files.each do |file|
|
45
|
+
results << @conn.put do |req|
|
46
|
+
req.url "/user/#{user}/lock"
|
47
|
+
req.headers['Content-Type'] = 'application/json'
|
48
|
+
req.body = JSON.generate({file: file})
|
49
|
+
end.body
|
50
|
+
end
|
51
|
+
results
|
52
|
+
end
|
53
|
+
|
54
|
+
def unlocks(user:, files:)
|
55
|
+
# TODO: requests at once
|
56
|
+
results = []
|
57
|
+
files.each do |file|
|
58
|
+
results << @conn.put do |req|
|
59
|
+
req.url "/user/#{user}/unlock"
|
60
|
+
req.headers['Content-Type'] = 'application/json'
|
61
|
+
req.body = JSON.generate({file: file})
|
62
|
+
end.body
|
63
|
+
end
|
64
|
+
results
|
65
|
+
end
|
66
|
+
|
67
|
+
def sync(dir: '.', force: false, includes: /\.(unity|prefab)$/, excludes: /^$/)
|
68
|
+
files = Find.find(dir).select{|it| it =~ includes}.select{|it| it !~ excludes}
|
69
|
+
|
70
|
+
puts "touched:"
|
71
|
+
puts files
|
72
|
+
|
73
|
+
touches(files)
|
74
|
+
touched_files = JSON.parse(search.body).collect {|it| it['file']}
|
75
|
+
|
76
|
+
if force
|
77
|
+
remove_files = touched_files.select{|it| !files.include?(it) }
|
78
|
+
|
79
|
+
puts "removed:"
|
80
|
+
puts remove_files
|
81
|
+
|
82
|
+
deletes(remove_files)
|
83
|
+
return (touched_files - remove_files)
|
84
|
+
end
|
85
|
+
|
86
|
+
return touched_files
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'json/add/core'
|
2
|
+
|
3
|
+
module Unitylock
|
4
|
+
module Server
|
5
|
+
module Entity
|
6
|
+
class UnityFile < Struct.new(:user,:file,:updated_at)
|
7
|
+
def update
|
8
|
+
self.updated_at = Time.now
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_hash
|
13
|
+
{
|
14
|
+
user: self.user,
|
15
|
+
file: self.file,
|
16
|
+
updated_at: self.updated_at.strftime("%Y-%m-%d %H:%M:%S")
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def json_create(o)
|
22
|
+
new(*o['v'])
|
23
|
+
end
|
24
|
+
|
25
|
+
def create(hash)
|
26
|
+
unityfile = UnityFile.new
|
27
|
+
default_values = { updated_at: Time.now }
|
28
|
+
default_values.merge(hash).each do |key,value|
|
29
|
+
unityfile[key] = value
|
30
|
+
end
|
31
|
+
|
32
|
+
unityfile
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'json'
|
3
|
+
require 'unitylock/server/entity/unityfile'
|
4
|
+
|
5
|
+
include Unitylock::Server::Entity
|
6
|
+
module Unitylock
|
7
|
+
module Server
|
8
|
+
class Model
|
9
|
+
attr_accessor :data, :file
|
10
|
+
|
11
|
+
def initialize(file)
|
12
|
+
@file = file
|
13
|
+
File.write(@file, '{}') unless File.exists? @file
|
14
|
+
load
|
15
|
+
end
|
16
|
+
|
17
|
+
def load
|
18
|
+
@data = JSON.parse(File.read(@file), create_additions: true)
|
19
|
+
end
|
20
|
+
|
21
|
+
def save
|
22
|
+
File.write(@file, JSON.generate(@data))
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete(file:)
|
26
|
+
@data.delete(file)
|
27
|
+
save
|
28
|
+
end
|
29
|
+
|
30
|
+
def lock(file:, user:)
|
31
|
+
raise "lock failure" unless @data.has_key?(file) && @data[file].user == nil
|
32
|
+
@data[file] = UnityFile.create(file: file, user: user)
|
33
|
+
save
|
34
|
+
@data[file]
|
35
|
+
end
|
36
|
+
|
37
|
+
def unlock(file:, user:)
|
38
|
+
raise "unlock failure" unless @data.has_key?(file) && @data[file].user == user
|
39
|
+
@data[file] = UnityFile.create(file: file, user: nil)
|
40
|
+
save
|
41
|
+
@data[file]
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_or_update(file:)
|
45
|
+
@data[file] ||= UnityFile.create(file: file)
|
46
|
+
@data[file].update
|
47
|
+
save
|
48
|
+
end
|
49
|
+
|
50
|
+
def search
|
51
|
+
@data.map {|key,value| value}
|
52
|
+
end
|
53
|
+
|
54
|
+
def fetch(file:)
|
55
|
+
@data[file]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|