mirror of
https://github.com/Dahlgren/arma-server-web-admin.git
synced 2024-08-30 17:22:10 +00:00
Steam Workshop Mods
This commit is contained in:
parent
7b6d0c6425
commit
08beb108a4
38
app.js
38
app.js
@ -1,4 +1,5 @@
|
||||
var express = require('express')
|
||||
var fs = require('fs')
|
||||
var bodyParser = require('body-parser')
|
||||
var morgan = require('morgan')
|
||||
var path = require('path')
|
||||
@ -11,7 +12,7 @@ var webpackConfig = require('./webpack.config')
|
||||
var setupBasicAuth = require('./lib/setup-basic-auth')
|
||||
var Manager = require('./lib/manager')
|
||||
var Missions = require('./lib/missions')
|
||||
var Mods = require('./lib/mods')
|
||||
var SteamMods = require('./lib/steam_mods')
|
||||
var Logs = require('./lib/logs')
|
||||
var Settings = require('./lib/settings')
|
||||
|
||||
@ -29,17 +30,44 @@ app.use(morgan(config.logFormat || 'dev'))
|
||||
|
||||
app.use(serveStatic(path.join(__dirname, 'public')))
|
||||
|
||||
/*
|
||||
Workaround for Steam Workshop with Linux Arma server
|
||||
|
||||
Absolute paths are not supported.
|
||||
Create symlink in Arma folder to Workshop folder.
|
||||
Rewrite Workshop mods to use relative path to symlinked folder instead.
|
||||
*/
|
||||
if (config.type === 'linux' && config.steam && config.steam.path) {
|
||||
var tempWorkshopFolder = path.join(config.path, 'workshop')
|
||||
try {
|
||||
var stat = fs.lstatSync(tempWorkshopFolder)
|
||||
if (!stat.isSymbolicLink()) {
|
||||
console.error('Please remove workshop folder from Arma directory manually and restart application')
|
||||
process.exit(1)
|
||||
}
|
||||
fs.unlinkSync(tempWorkshopFolder)
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
console.error('Something went wrong when creating workaround for workshop')
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
fs.symlinkSync(config.steam.path, tempWorkshopFolder)
|
||||
}
|
||||
|
||||
var logs = new Logs(config)
|
||||
|
||||
var manager = new Manager(config, logs)
|
||||
manager.load()
|
||||
|
||||
var missions = new Missions(config)
|
||||
var mods = new Mods(config)
|
||||
var mods = new SteamMods(config)
|
||||
mods.updateMods()
|
||||
|
||||
var settings = new Settings(config)
|
||||
|
||||
var manager = new Manager(config, logs, mods)
|
||||
manager.load()
|
||||
|
||||
app.use('/api/logs', require('./routes/logs')(logs))
|
||||
app.use('/api/missions', require('./routes/missions')(missions))
|
||||
app.use('/api/mods', require('./routes/mods')(mods))
|
||||
|
@ -13,6 +13,12 @@ module.exports = {
|
||||
'@mod1',
|
||||
'@mod2',
|
||||
],
|
||||
steam: {
|
||||
apiKey: '1234567890ABCDE', // https://steamcommunity.com/dev/apikey
|
||||
path: 'path-to-main-steam-folder',
|
||||
username: 'steam_username',
|
||||
password: 'steam_password',
|
||||
},
|
||||
admins: [], // add steam IDs here to enable #login without password
|
||||
auth: { // If both username and password is set, HTTP Basic Auth will be used. You may use an array to specify more than one user.
|
||||
username: '', // Username for HTTP Basic Auth
|
||||
|
@ -5,9 +5,10 @@ var Server = require('./server')
|
||||
|
||||
var filePath = 'servers.json'
|
||||
|
||||
var Manager = function (config, logs) {
|
||||
var Manager = function (config, logs, mods) {
|
||||
this.config = config
|
||||
this.logs = logs
|
||||
this.mods = mods
|
||||
this.serversArr = []
|
||||
this.serversHash = {}
|
||||
}
|
||||
@ -41,7 +42,7 @@ Manager.prototype.removeServer = function (id) {
|
||||
}
|
||||
|
||||
Manager.prototype._addServer = function (data) {
|
||||
var server = new Server(this.config, this.logs, data)
|
||||
var server = new Server(this.config, this.logs, this.mods, data)
|
||||
this.serversArr.push(server)
|
||||
this.serversArr.sort(function (a, b) {
|
||||
return a.title.localeCompare(b.title)
|
||||
|
10
lib/mods.js
10
lib/mods.js
@ -21,6 +21,12 @@ Mods.prototype.delete = function (mod, cb) {
|
||||
})
|
||||
}
|
||||
|
||||
Mods.prototype.find = function (id) {
|
||||
this.mods.find(function (mod) {
|
||||
return mod.id === id
|
||||
})
|
||||
}
|
||||
|
||||
Mods.prototype.updateMods = function () {
|
||||
var self = this
|
||||
glob('**/{@*,csla,gm,vn}/addons', { cwd: self.config.path }, function (err, files) {
|
||||
@ -28,9 +34,11 @@ Mods.prototype.updateMods = function () {
|
||||
console.log(err)
|
||||
} else {
|
||||
var mods = files.map(function (file) {
|
||||
var name = path.join(file, '..')
|
||||
return {
|
||||
// Find actual parent mod folder from addons folder
|
||||
name: path.join(file, '..')
|
||||
id: name,
|
||||
name: name
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -17,9 +17,10 @@ var queryTypes = {
|
||||
ofpresistance: 'operationflashpoint'
|
||||
}
|
||||
|
||||
var Server = function (config, logs, options) {
|
||||
var Server = function (config, logs, mods, options) {
|
||||
this.config = config
|
||||
this.logs = logs
|
||||
this.modsManager = mods
|
||||
this.update(options)
|
||||
}
|
||||
|
||||
@ -95,6 +96,21 @@ Server.prototype.queryStatus = function () {
|
||||
)
|
||||
}
|
||||
|
||||
Server.prototype.getMods = function () {
|
||||
var self = this
|
||||
return this.mods.map(function (mod) {
|
||||
return self.modsManager.find(mod)
|
||||
}).filter(function (mod) {
|
||||
return mod
|
||||
}).map(function (mod) {
|
||||
if (self.config.type === 'linux' && self.config.steam && self.config.steam.path) {
|
||||
return mod.path.replace(self.config.steam.path, 'workshop/')
|
||||
}
|
||||
|
||||
return mod.path
|
||||
})
|
||||
}
|
||||
|
||||
Server.prototype.getParameters = function () {
|
||||
var parameters = []
|
||||
|
||||
@ -132,6 +148,7 @@ Server.prototype.start = function () {
|
||||
return this
|
||||
}
|
||||
|
||||
var mods = this.getMods()
|
||||
var parameters = this.getParameters()
|
||||
var server = new ArmaServer.Server({
|
||||
additionalConfigurationOptions: this.getAdditionalConfigurationOptions(),
|
||||
@ -147,7 +164,7 @@ Server.prototype.start = function () {
|
||||
hostname: this.createServerTitle(this.title),
|
||||
localClient: this.number_of_headless_clients > 0 ? ['127.0.0.1'] : null,
|
||||
missions: this.missions,
|
||||
mods: this.mods,
|
||||
mods: mods,
|
||||
motd: (this.motd && this.motd.split('\n')) || null,
|
||||
parameters: parameters,
|
||||
password: this.password,
|
||||
@ -197,6 +214,7 @@ Server.prototype.startHeadlessClientsIfNeeded = function () {
|
||||
}
|
||||
|
||||
Server.prototype.startHeadlessClients = function () {
|
||||
var mods = this.getMods()
|
||||
var parameters = this.getParameters()
|
||||
var self = this
|
||||
var headlessClientInstances = _.times(this.number_of_headless_clients, function (i) {
|
||||
@ -204,7 +222,7 @@ Server.prototype.startHeadlessClients = function () {
|
||||
filePatching: self.file_patching,
|
||||
game: self.config.game,
|
||||
host: '127.0.0.1',
|
||||
mods: self.mods,
|
||||
mods: mods,
|
||||
parameters: parameters,
|
||||
password: self.password,
|
||||
path: self.config.path,
|
||||
|
96
lib/steam_mods.js
Normal file
96
lib/steam_mods.js
Normal file
@ -0,0 +1,96 @@
|
||||
var events = require('events')
|
||||
var ArmaSteamWorkshop = require('arma-steam-workshop')
|
||||
|
||||
var SteamMods = function (config) {
|
||||
this.config = config
|
||||
this.armaSteamWorkshop = new ArmaSteamWorkshop(this.config.steam)
|
||||
this.mods = []
|
||||
}
|
||||
|
||||
SteamMods.prototype = new events.EventEmitter()
|
||||
|
||||
SteamMods.prototype.delete = function (mod, cb) {
|
||||
var self = this
|
||||
this.armaSteamWorkshop.deleteMod(mod, function (err) {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
} else {
|
||||
self.updateMods()
|
||||
}
|
||||
|
||||
if (cb) {
|
||||
cb(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
SteamMods.prototype.find = function (id) {
|
||||
return this.mods.find(function (mod) {
|
||||
return mod.id === id
|
||||
})
|
||||
}
|
||||
|
||||
SteamMods.prototype.download = function (workshopId, cb) {
|
||||
var self = this
|
||||
this.armaSteamWorkshop.downloadMod(workshopId, function (err) {
|
||||
self.addStatusForCurrentDowloads()
|
||||
self.updateMods()
|
||||
|
||||
if (cb) {
|
||||
cb(err)
|
||||
}
|
||||
})
|
||||
self.addStatusForCurrentDowloads()
|
||||
self.emit('mods', this.mods)
|
||||
}
|
||||
|
||||
SteamMods.prototype.search = function (query, cb) {
|
||||
this.armaSteamWorkshop.search(query, cb)
|
||||
}
|
||||
|
||||
SteamMods.prototype.updateMods = function () {
|
||||
var self = this
|
||||
this.armaSteamWorkshop.mods(function (err, mods) {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
} else {
|
||||
self.mods = mods
|
||||
self.addStatusForCurrentDowloads()
|
||||
self.emit('mods', mods)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
SteamMods.prototype.isModDownloading = function (workshopId) {
|
||||
return this.armaSteamWorkshop.currentDownloads[workshopId] === true
|
||||
}
|
||||
|
||||
SteamMods.prototype.addStatusForCurrentDowloads = function () {
|
||||
var self = this
|
||||
this.mods.forEach(function (mod) {
|
||||
mod.downloading = self.isModDownloading(mod.id)
|
||||
})
|
||||
|
||||
this.addDummyModsForCurrentDowloads()
|
||||
this.emit('mods', this.mods)
|
||||
}
|
||||
|
||||
SteamMods.prototype.addDummyModsForCurrentDowloads = function () {
|
||||
var self = this
|
||||
for (var workshopId in this.armaSteamWorkshop.currentDownloads) {
|
||||
var mod = self.mods.find(function (mod) {
|
||||
return mod.id === workshopId
|
||||
})
|
||||
|
||||
if (!mod) {
|
||||
self.mods.splice(0, 0, {
|
||||
id: workshopId,
|
||||
name: workshopId,
|
||||
path: null,
|
||||
downloading: true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SteamMods
|
@ -18,6 +18,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"arma-server": "0.0.10",
|
||||
"arma-steam-workshop": "https://github.com/Dahlgren/node-arma-steam-workshop/tarball/0396b9941eb43f714bd11df2bc5a99f0b283313e",
|
||||
"async": "^0.9.0",
|
||||
"backbone": "1.3.3",
|
||||
"backbone.bootstrap-modal": "https://github.com/powmedia/backbone.bootstrap-modal/archive/632210077c2424be2ee6ea2aafe0d3fe016ae524.tar.gz",
|
||||
|
@ -2,8 +2,11 @@ var Backbone = require('backbone')
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
defaults: {
|
||||
name: ''
|
||||
id: '',
|
||||
downloading: false,
|
||||
name: '',
|
||||
path: ''
|
||||
},
|
||||
idAttribute: 'name',
|
||||
idAttribute: 'id',
|
||||
urlRoot: '/api/mods/'
|
||||
})
|
||||
|
82
public/js/app/views/mods/download/form.js
Normal file
82
public/js/app/views/mods/download/form.js
Normal file
@ -0,0 +1,82 @@
|
||||
var $ = require('jquery')
|
||||
var _ = require('underscore')
|
||||
var Marionette = require('marionette')
|
||||
var Ladda = require('ladda')
|
||||
var Mods = require('app/collections/mods')
|
||||
var tpl = require('tpl/mods/download/form.html')
|
||||
var sweetAlert = require('sweet-alert')
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
|
||||
events: {
|
||||
submit: 'beforeSubmit'
|
||||
},
|
||||
|
||||
template: _.template(tpl),
|
||||
|
||||
initialize: function (options) {
|
||||
this.mods = options.mods
|
||||
this.collection = new Mods()
|
||||
this.bind('ok', this.submit)
|
||||
this.bind('shown', this.shown)
|
||||
|
||||
var self = this
|
||||
this.listenTo(this.mods, 'change reset add remove', function () {
|
||||
self.collection.trigger('reset')
|
||||
})
|
||||
},
|
||||
|
||||
beforeSubmit: function (e) {
|
||||
e.preventDefault()
|
||||
this.submit()
|
||||
},
|
||||
|
||||
shown: function (modal) {
|
||||
var $okBtn = modal.$el.find('.btn.ok')
|
||||
$okBtn.addClass('ladda-button').attr('data-style', 'expand-left')
|
||||
|
||||
this.laddaBtn = Ladda.create($okBtn.get(0))
|
||||
|
||||
this.$el.find('form .query').focus()
|
||||
},
|
||||
|
||||
submit: function (modal) {
|
||||
var self = this
|
||||
var $form = this.$el.find('form')
|
||||
|
||||
if (modal) {
|
||||
self.modal.preventClose()
|
||||
}
|
||||
|
||||
$form.find('.form-group').removeClass('has-error')
|
||||
$form.find('.help-block').text('')
|
||||
|
||||
this.laddaBtn.start()
|
||||
self.modal.$el.find('.btn.cancel').addClass('disabled')
|
||||
|
||||
$.ajax({
|
||||
url: '/api/mods/',
|
||||
type: 'POST',
|
||||
data: {
|
||||
id: $form.find('.query').val()
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function (resp) {
|
||||
self.laddaBtn.stop()
|
||||
self.modal.$el.find('.ladda-button').removeClass('disabled')
|
||||
self.modal.close()
|
||||
},
|
||||
error: function (resp) {
|
||||
self.laddaBtn.stop()
|
||||
self.modal.$el.find('.ladda-button').removeClass('disabled')
|
||||
self.modal.close()
|
||||
|
||||
sweetAlert({
|
||||
title: 'Error',
|
||||
text: 'An error occurred, please consult the logs',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
@ -1,8 +1,11 @@
|
||||
var $ = require('jquery')
|
||||
var _ = require('underscore')
|
||||
var Marionette = require('marionette')
|
||||
var BootstrapModal = require('backbone.bootstrap-modal')
|
||||
|
||||
var ListItemView = require('app/views/mods/list_item')
|
||||
var DownloadFormView = require('app/views/mods/download/form')
|
||||
var SearchFormView = require('app/views/mods/search/form')
|
||||
var tpl = require('tpl/mods/list.html')
|
||||
|
||||
var template = _.template(tpl)
|
||||
@ -13,7 +16,22 @@ module.exports = Marionette.CompositeView.extend({
|
||||
template: template,
|
||||
|
||||
events: {
|
||||
'click #refresh': 'refresh'
|
||||
'click #download': 'download',
|
||||
'click #refresh': 'refresh',
|
||||
'click #search': 'search'
|
||||
},
|
||||
|
||||
download: function (event) {
|
||||
event.preventDefault()
|
||||
var view = new DownloadFormView({ mods: this.collection })
|
||||
var modal = new BootstrapModal({
|
||||
content: view,
|
||||
animate: true,
|
||||
cancelText: 'Close',
|
||||
okText: 'Download'
|
||||
})
|
||||
view.modal = modal
|
||||
modal.open()
|
||||
},
|
||||
|
||||
refresh: function (event) {
|
||||
@ -28,5 +46,18 @@ module.exports = Marionette.CompositeView.extend({
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
search: function (event) {
|
||||
event.preventDefault()
|
||||
var view = new SearchFormView({ mods: this.collection })
|
||||
var modal = new BootstrapModal({
|
||||
content: view,
|
||||
animate: true,
|
||||
cancelText: 'Close',
|
||||
okText: 'Search'
|
||||
})
|
||||
view.modal = modal
|
||||
modal.open()
|
||||
}
|
||||
})
|
||||
|
@ -14,6 +14,10 @@ module.exports = Marionette.ItemView.extend({
|
||||
'click .destroy': 'deleteMod'
|
||||
},
|
||||
|
||||
modelEvents: {
|
||||
change: 'render'
|
||||
},
|
||||
|
||||
deleteMod: function (event) {
|
||||
var self = this
|
||||
sweetAlert({
|
||||
|
90
public/js/app/views/mods/search/form.js
Normal file
90
public/js/app/views/mods/search/form.js
Normal file
@ -0,0 +1,90 @@
|
||||
var $ = require('jquery')
|
||||
var _ = require('underscore')
|
||||
var Marionette = require('marionette')
|
||||
var ListItemView = require('app/views/mods/search/list_item')
|
||||
var Ladda = require('ladda')
|
||||
var Mods = require('app/collections/mods')
|
||||
var tpl = require('tpl/mods/search/form.html')
|
||||
var sweetAlert = require('sweet-alert')
|
||||
|
||||
module.exports = Marionette.CompositeView.extend({
|
||||
|
||||
events: {
|
||||
submit: 'beforeSubmit'
|
||||
},
|
||||
|
||||
childView: ListItemView,
|
||||
childViewContainer: 'tbody',
|
||||
template: _.template(tpl),
|
||||
|
||||
initialize: function (options) {
|
||||
this.mods = options.mods
|
||||
this.collection = new Mods()
|
||||
this.bind('ok', this.submit)
|
||||
this.bind('shown', this.shown)
|
||||
|
||||
var self = this
|
||||
this.listenTo(this.mods, 'change reset add remove', function () {
|
||||
self.collection.trigger('reset')
|
||||
})
|
||||
},
|
||||
|
||||
childViewOptions: function (options) {
|
||||
options.set('mods', this.mods)
|
||||
},
|
||||
|
||||
beforeSubmit: function (e) {
|
||||
e.preventDefault()
|
||||
this.submit()
|
||||
},
|
||||
|
||||
shown: function (modal) {
|
||||
var $okBtn = modal.$el.find('.btn.ok')
|
||||
$okBtn.addClass('ladda-button').attr('data-style', 'expand-left')
|
||||
|
||||
this.laddaBtn = Ladda.create($okBtn.get(0))
|
||||
|
||||
this.$el.find('form .query').focus()
|
||||
},
|
||||
|
||||
submit: function (modal) {
|
||||
var self = this
|
||||
var $form = this.$el.find('form')
|
||||
|
||||
if (modal) {
|
||||
self.modal.preventClose()
|
||||
}
|
||||
|
||||
$form.find('.form-group').removeClass('has-error')
|
||||
$form.find('.help-block').text('')
|
||||
|
||||
this.laddaBtn.start()
|
||||
self.modal.$el.find('.btn.cancel').addClass('disabled')
|
||||
|
||||
$.ajax({
|
||||
url: '/api/mods/search',
|
||||
type: 'POST',
|
||||
data: {
|
||||
query: $form.find('.query').val()
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
self.laddaBtn.stop()
|
||||
self.modal.$el.find('.btn.cancel').removeClass('disabled')
|
||||
self.collection.set(data)
|
||||
},
|
||||
error: function () {
|
||||
self.laddaBtn.stop()
|
||||
$form.find('.form-group').addClass('has-error')
|
||||
$form.find('.help-block').text('Problem searching, try again')
|
||||
self.modal.$el.find('.btn.cancel').removeClass('disabled')
|
||||
|
||||
sweetAlert({
|
||||
title: 'Error',
|
||||
text: 'An error occurred, please consult the logs',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
52
public/js/app/views/mods/search/list_item.js
Normal file
52
public/js/app/views/mods/search/list_item.js
Normal file
@ -0,0 +1,52 @@
|
||||
var $ = require('jquery')
|
||||
var _ = require('underscore')
|
||||
var Marionette = require('marionette')
|
||||
var Ladda = require('ladda')
|
||||
var tpl = require('tpl/mods/search/list_item.html')
|
||||
|
||||
var template = _.template(tpl)
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
tagName: 'tr',
|
||||
template: template,
|
||||
|
||||
events: {
|
||||
'click .install': 'install'
|
||||
},
|
||||
|
||||
templateHelpers: {
|
||||
downloading: function () {
|
||||
if (this.mods.get(this.id)) {
|
||||
return this.mods.get(this.id).get('downloading')
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
install: function (event) {
|
||||
var self = this
|
||||
event.preventDefault()
|
||||
|
||||
this.laddaBtn = Ladda.create(this.$el.find('.ladda-button').get(0))
|
||||
this.laddaBtn.start()
|
||||
this.$el.find('.ladda-button').addClass('disabled')
|
||||
|
||||
$.ajax({
|
||||
url: '/api/mods/',
|
||||
type: 'POST',
|
||||
data: {
|
||||
id: this.model.get('id')
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function (resp) {
|
||||
self.laddaBtn.stop()
|
||||
self.$el.find('.ladda-button').removeClass('disabled')
|
||||
},
|
||||
error: function (resp) {
|
||||
self.laddaBtn.stop()
|
||||
self.$el.find('.ladda-button').removeClass('disabled')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
@ -57,5 +57,16 @@ module.exports = Marionette.LayoutView.extend({
|
||||
self.render()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
templateHelpers: function () {
|
||||
var self = this
|
||||
return {
|
||||
mods: self.options.mods.filter(function (mod) {
|
||||
return self.model.get('mods').indexOf(mod.get('id')) >= 0
|
||||
}).map(function (mod) {
|
||||
return mod.get('name')
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -11,7 +11,7 @@ module.exports = ModListItemView.extend({
|
||||
|
||||
templateHelpers: function () {
|
||||
return {
|
||||
enabled: this.options.server.get('mods').indexOf(this.model.get('name')) > -1
|
||||
enabled: this.options.server.get('mods').indexOf(this.model.get('id')) > -1
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -39,7 +39,7 @@ module.exports = Marionette.LayoutView.extend({
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.infoView.show(new InfoView({ model: this.model }))
|
||||
this.infoView.show(new InfoView({ model: this.model, mods: this.mods }))
|
||||
this.missionsView.show(new MissionsView({ missions: this.missions, model: this.model }))
|
||||
this.modsView.show(new ModsListView({ collection: this.mods, server: this.model }))
|
||||
this.parametersView.show(new ParametersListView({ model: this.model }))
|
||||
|
11
public/js/tpl/mods/download/form.html
Normal file
11
public/js/tpl/mods/download/form.html
Normal file
@ -0,0 +1,11 @@
|
||||
<p>
|
||||
Install mod from <a href='https://steamcommunity.com/app/107410/workshop/' target=_blank>Steam Workshop</a> by id
|
||||
</p>
|
||||
|
||||
<form class="form" role="form" action="/api/mods" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="query" class="control-label">ID</label>
|
||||
<input type="text" class="form-control query" name="query" id="query">
|
||||
<span class="help-block"></span>
|
||||
</div>
|
||||
</form>
|
@ -3,6 +3,16 @@
|
||||
Refresh
|
||||
</a>
|
||||
|
||||
<a class="btn btn-primary" id="download" href="#">
|
||||
<span class="glyphicon glyphicon-search"></span>
|
||||
Download specific mod
|
||||
</a>
|
||||
|
||||
<a class="btn btn-primary" id="search" href="#">
|
||||
<span class="glyphicon glyphicon-search"></span>
|
||||
Search & Download
|
||||
</a>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -2,10 +2,16 @@
|
||||
<a href='#mods/<%-name%>'><%-name%></a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn-danger btn-xs destroy ladda-button pull-right" data-style="expand-left">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
Delete
|
||||
</a>
|
||||
<% if (downloading) { %>
|
||||
<div class="progress" style="margin-bottom: 0;">
|
||||
<div class="progress-bar progress-bar-striped active" role="progressbar" style="width: 100%"></div>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<a class="btn btn-danger btn-xs destroy ladda-button pull-right" data-style="expand-left">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
Delete
|
||||
</a>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div class="clearfix"></div>
|
||||
<% } %>
|
||||
</td>
|
||||
|
23
public/js/tpl/mods/search/form.html
Normal file
23
public/js/tpl/mods/search/form.html
Normal file
@ -0,0 +1,23 @@
|
||||
<p>
|
||||
Search mods on <a href='https://steamcommunity.com/app/107410/workshop/' target=_blank>Steam Workshop</a>
|
||||
</p>
|
||||
|
||||
<form class="form" role="form" action="/api/mods" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="query" class="control-label">Search</label>
|
||||
<input type="text" class="form-control query" name="query" id="query">
|
||||
<span class="help-block"></span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mod</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<!-- want to insert collection items, here -->
|
||||
<tbody></tbody>
|
||||
</table>
|
27
public/js/tpl/mods/search/list_item.html
Normal file
27
public/js/tpl/mods/search/list_item.html
Normal file
@ -0,0 +1,27 @@
|
||||
<td style="width: 100%;">
|
||||
<p>
|
||||
<strong><a href="<%-link%>" target=_blank><%-title%></a></strong>
|
||||
</p>
|
||||
<p>
|
||||
<small><%-description%></strong>
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
<% if (downloading()) { %>
|
||||
<div class="progress" style="margin-bottom: 0;">
|
||||
<div class="progress-bar progress-bar-striped active" role="progressbar" style="width: 100%"></div>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<a class="btn btn-primary btn-xs install ladda-button pull-right" data-style="expand-left">
|
||||
<span class="glyphicon glyphicon-save"></span>
|
||||
Install
|
||||
</a>
|
||||
<% } %>
|
||||
<div class="clearfix"></div>
|
||||
</p>
|
||||
<p>
|
||||
<small class="pull-right"><%-fileSizeFormatted%></small>
|
||||
<div class="clearfix"></div>
|
||||
</p>
|
||||
</td>
|
@ -1,7 +1,7 @@
|
||||
<td>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" value="<%-name%>" <% if (enabled) { %>checked<% } %> >
|
||||
<input type="checkbox" value="<%-id%>" <% if (enabled) { %>checked<% } %> >
|
||||
<%-name%>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -7,6 +7,16 @@ module.exports = function (modsManager) {
|
||||
res.send(modsManager.mods)
|
||||
})
|
||||
|
||||
router.post('/', function (req, res) {
|
||||
modsManager.download(req.body.id)
|
||||
res.status(204).send()
|
||||
})
|
||||
|
||||
router.put('/:mod', function (req, res) {
|
||||
modsManager.download(req.params.mod)
|
||||
res.status(204).send()
|
||||
})
|
||||
|
||||
router.delete('/:mod', function (req, res) {
|
||||
modsManager.delete(req.params.mod, function (err) {
|
||||
if (err) {
|
||||
@ -22,5 +32,16 @@ module.exports = function (modsManager) {
|
||||
res.status(204).send()
|
||||
})
|
||||
|
||||
router.post('/search', function (req, res) {
|
||||
var query = req.body.query || ''
|
||||
modsManager.search(query, function (err, mods) {
|
||||
if (err || !mods) {
|
||||
res.status(500).send(err)
|
||||
} else {
|
||||
res.send(mods)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ var Server = require('../../lib/server.js')
|
||||
describe('Server', function () {
|
||||
describe('generateId()', function () {
|
||||
it('should include title', function () {
|
||||
var server = new Server(null, null, { title: 'title.with.lot.of.dots' })
|
||||
var server = new Server(null, null, null, { title: 'title.with.lot.of.dots' })
|
||||
server.generateId().should.eql('title-with-lot-of-dots')
|
||||
})
|
||||
})
|
||||
|
||||
describe('toJSON()', function () {
|
||||
it('should include title', function () {
|
||||
var server = new Server(null, null, { title: 'test' })
|
||||
var server = new Server(null, null, null, { title: 'test' })
|
||||
server.toJSON().should.have.property('title', 'test')
|
||||
})
|
||||
})
|
||||
|
21
test/lib/steam_mods.js
Normal file
21
test/lib/steam_mods.js
Normal file
@ -0,0 +1,21 @@
|
||||
require('should')
|
||||
|
||||
var SteamMods = require('../../lib/steam_mods.js')
|
||||
|
||||
var dummyMods = [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'test'
|
||||
}
|
||||
]
|
||||
|
||||
describe('SteamMods', function () {
|
||||
describe('find()', function () {
|
||||
it('should find mod', function () {
|
||||
var steamMods = new SteamMods({})
|
||||
steamMods.mods = dummyMods
|
||||
var mod = steamMods.find('test')
|
||||
mod.should.eql(dummyMods[0])
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user