unitylock 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|