Merge pull request #978 from openHPI/feature/subpath

Add config options for running under subpath
This commit is contained in:
Sebastian Serth
2021-07-07 18:35:44 +02:00
committed by GitHub
22 changed files with 89 additions and 28 deletions

View File

@ -16,6 +16,7 @@ gem 'highline'
gem 'i18n-js' gem 'i18n-js'
gem 'ims-lti', '< 2.0.0' gem 'ims-lti', '< 2.0.0'
gem 'jbuilder' gem 'jbuilder'
gem 'js-routes'
gem 'kramdown' gem 'kramdown'
gem 'mimemagic' gem 'mimemagic'
gem 'nokogiri' gem 'nokogiri'

View File

@ -216,6 +216,8 @@ GEM
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
jquery-ui-rails (6.0.1) jquery-ui-rails (6.0.1)
railties (>= 3.2.16) railties (>= 3.2.16)
js-routes (2.0.7)
railties (>= 4)
json (2.3.1) json (2.3.1)
jwt (2.2.3) jwt (2.2.3)
kaminari (1.2.1) kaminari (1.2.1)
@ -554,6 +556,7 @@ DEPENDENCIES
i18n-js i18n-js
ims-lti (< 2.0.0) ims-lti (< 2.0.0)
jbuilder jbuilder
js-routes
kramdown kramdown
listen listen
mimemagic mimemagic

View File

@ -46,7 +46,8 @@ In order to execute code submissions using Docker, source code files are written
## Production Setup ## Production Setup
- We recommend using [Capistrano](http://capistranorb.com/) for deployment - We recommend using [Capistrano](http://capistranorb.com/) for deployment.
- Once deployed, CodeOcean assumes to run exclusively under a (sub)domain. If you want to use it under a custom subpath, you can specify the desired path using an environment variable: `RAILS_RELATIVE_URL_ROOT=/codeocean`. Please ensure to rebuild all assets and restart the server to apply the new path.
## Monitoring ## Monitoring
- We use a [Prometheus Exporter](https://github.com/discourse/prometheus_exporter) and a [Telegraf Client](https://github.com/jgraichen/telegraf-ruby) - We use a [Prometheus Exporter](https://github.com/discourse/prometheus_exporter) and a [Telegraf Client](https://github.com/jgraichen/telegraf-ruby)

View File

@ -51,7 +51,7 @@ $(document).on('turbolinks:load', function() {
} else { } else {
var jqxhr = $.ajax({ var jqxhr = $.ajax({
dataType: 'json', dataType: 'json',
url: '/admin/dashboard', url: Routes.admin_dashboard_path(),
method: 'GET' method: 'GET'
}); });
jqxhr.done(function(response) { jqxhr.done(function(response) {

View File

@ -1,7 +1,7 @@
var CodeOceanEditor = { var CodeOceanEditor = {
//ACE-Editor-Path //ACE-Editor-Path
// ruby part adds the relative_url_root, if it is set. // ruby part adds the relative_url_root, if it is set.
ACE_FILES_PATH: '<%= (defined? Rails.application.config.relative_url_root) && Rails.application.config.relative_url_root != nil && Rails.application.config.relative_url_root != "" ? Rails.application.config.relative_url_root : "" %>' + '/assets/ace/', ACE_FILES_PATH: '<%= "#{Rails.application.config.relative_url_root.chomp('/')}/assets/ace/" %>',
THEME: 'ace/theme/textmate', THEME: 'ace/theme/textmate',
//Color-Encoding for Percentages in Progress Bars (For submissions) //Color-Encoding for Percentages in Progress Bars (For submissions)

View File

@ -142,7 +142,7 @@ CodeOceanEditorRequestForComments = {
var createRequestForComments = function (submission) { var createRequestForComments = function (submission) {
$.ajax({ $.ajax({
method: 'POST', method: 'POST',
url: '/request_for_comments', url: Routes.request_for_comments_path(),
data: { data: {
request_for_comment: { request_for_comment: {
exercise_id: exercise_id, exercise_id: exercise_id,

View File

@ -1,6 +1,6 @@
$(document).on('turbolinks:load', function () { $(document).on('turbolinks:load', function () {
// ruby part adds the relative_url_root, if it is set. // ruby part adds the relative_url_root, if it is set.
var ACE_FILES_PATH = '<%= (defined? Rails.application.config.relative_url_root) && Rails.application.config.relative_url_root != nil && Rails.application.config.relative_url_root != "" ? Rails.application.config.relative_url_root : "" %>' + '/assets/ace/'; var ACE_FILES_PATH = '<%= "#{Rails.application.config.relative_url_root.chomp('/')}/assets/ace/" %>';
var THEME = 'ace/theme/textmate'; var THEME = 'ace/theme/textmate';
var TAB_KEY_CODE = 9; var TAB_KEY_CODE = 9;
@ -351,7 +351,7 @@ $(document).on('turbolinks:load', function () {
return $.ajax({ return $.ajax({
type: 'POST', type: 'POST',
url: '/exercises/' + exerciseID + '/export_external_check', url: Routes.export_external_check_exercise_path(exerciseID),
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
$messageDiv.html(response.message); $messageDiv.html(response.message);
@ -370,7 +370,7 @@ $(document).on('turbolinks:load', function () {
return $.ajax({ return $.ajax({
type: 'POST', type: 'POST',
url: '/exercises/' + exerciseID + '/export_external_confirm', url: Routes.export_external_confirm_exercise_path(exerciseID),
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
$messageDiv.html(response.message) $messageDiv.html(response.message)
@ -427,9 +427,8 @@ $(document).on('turbolinks:load', function () {
}; };
var updateFileTemplates = function (fileType) { var updateFileTemplates = function (fileType) {
var rel_url_root = '<%= (defined? Rails.application.config.relative_url_root) && Rails.application.config.relative_url_root != nil && Rails.application.config.relative_url_root != "" ? Rails.application.config.relative_url_root : "" %>';
var jqxhr = $.ajax({ var jqxhr = $.ajax({
url: rel_url_root + '/file_templates/by_file_type/' + fileType + '.json', url: Routes.by_file_type_file_templates_path(fileType),
dataType: 'json' dataType: 'json'
}); });
jqxhr.done(function (response) { jqxhr.done(function (response) {

View File

@ -1,5 +1,5 @@
(function() { (function() {
var ACE_FILES_PATH = '/assets/ace/'; var ACE_FILES_PATH = '<%= "#{Rails.application.config.relative_url_root.chomp('/')}/assets/ace/" %>';
window.MarkdownEditor = function(selector) { window.MarkdownEditor = function(selector) {
ace.config.set('modePath', ACE_FILES_PATH); ace.config.set('modePath', ACE_FILES_PATH);

View File

@ -1,6 +1,6 @@
$(document).on('turbolinks:load', function() { $(document).on('turbolinks:load', function() {
var ACE_FILES_PATH = '/assets/ace/'; var ACE_FILES_PATH = '<%= "#{Rails.application.config.relative_url_root.chomp('/')}/assets/ace/" %>';
var THEME = 'ace/theme/textmate'; var THEME = 'ace/theme/textmate';
var currentSubmission = 0; var currentSubmission = 0;

View File

@ -44,7 +44,7 @@ module StatisticsHelper
data: ExternalUser.joins(:submissions) data: ExternalUser.joins(:submissions)
.where(['submissions.created_at >= ?', DateTime.now - 5.minutes]) .where(['submissions.created_at >= ?', DateTime.now - 5.minutes])
.distinct('external_users.id').count, .distinct('external_users.id').count,
url: 'statistics/graphs', url: statistics_graphs_path,
}, },
] ]
end end

View File

@ -39,3 +39,7 @@ import 'jquery-ui/themes/base/core.css'
import 'jquery-ui/themes/base/resizable.css' import 'jquery-ui/themes/base/resizable.css'
import 'jquery-ui/themes/base/selectable.css' import 'jquery-ui/themes/base/selectable.css'
import 'jquery-ui/themes/base/sortable.css' import 'jquery-ui/themes/base/sortable.css'
// Routes
import * as Routes from 'routes.js.erb';
window.Routes = Routes;

View File

@ -0,0 +1 @@
<%= JsRoutes.generate %>

View File

@ -148,7 +148,7 @@ javascript:
}); });
// set file paths for ace // set file paths for ace
var ACE_FILES_PATH = '/assets/ace/'; var ACE_FILES_PATH = "#{Rails.application.config.relative_url_root.chomp('/')}/assets/ace/";
_.each(['modePath', 'themePath', 'workerPath'], function(attribute) { _.each(['modePath', 'themePath', 'workerPath'], function(attribute) {
ace.config.set(attribute, ACE_FILES_PATH); ace.config.set(attribute, ACE_FILES_PATH);
}); });
@ -231,7 +231,7 @@ javascript:
var jqrequest = $.ajax({ var jqrequest = $.ajax({
dataType: 'json', dataType: 'json',
method: 'GET', method: 'GET',
url: '/comments', url: Routes.comments_path(),
data: { data: {
file_id: fileid file_id: fileid
} }
@ -254,7 +254,7 @@ javascript:
function deleteComment(commentId, editor, file_id, callback) { function deleteComment(commentId, editor, file_id, callback) {
var jqxhr = $.ajax({ var jqxhr = $.ajax({
type: 'DELETE', type: 'DELETE',
url: "/comments/" + commentId url: Routes.comments_path(commentId)
}); });
jqxhr.done(function () { jqxhr.done(function () {
setAnnotations(editor, file_id); setAnnotations(editor, file_id);
@ -266,7 +266,7 @@ javascript:
function updateComment(commentId, text, editor, file_id, callback) { function updateComment(commentId, text, editor, file_id, callback) {
var jqxhr = $.ajax({ var jqxhr = $.ajax({
type: 'PATCH', type: 'PATCH',
url: "/comments/" + commentId, url: Routes.comments_path(commentId),
data: { data: {
comment: { comment: {
text: text text: text
@ -293,7 +293,7 @@ javascript:
}, },
dataType: 'json', dataType: 'json',
method: 'POST', method: 'POST',
url: "/comments" url: Routes.comments_path()
}); });
jqxhr.done(function(){ jqxhr.done(function(){
setAnnotations(editor, file_id); setAnnotations(editor, file_id);
@ -312,7 +312,7 @@ javascript:
}, },
dataType: 'json', dataType: 'json',
method: 'POST', method: 'POST',
url: "/subscriptions.json" url: Routes.subscriptions_path({format: 'json'})
}); });
jqxhr.done(function(subscription) { jqxhr.done(function(subscription) {
checkbox.data('subscription', subscription.id); checkbox.data('subscription', subscription.id);
@ -329,7 +329,7 @@ javascript:
checkbox.attr("disabled", true); checkbox.attr("disabled", true);
var subscriptionId = checkbox.data('subscription'); var subscriptionId = checkbox.data('subscription');
var jqxhr = $.ajax({ var jqxhr = $.ajax({
url: '/subscriptions/' + subscriptionId + '/unsubscribe.json' url: Routes.unsubscribe_subscription_path(subscriptionId, {format: 'json'})
}); });
jqxhr.done(function(response) { jqxhr.done(function(response) {
checkbox.prop('checked', false); checkbox.prop('checked', false);

View File

@ -4,5 +4,7 @@
require_relative 'config/environment' require_relative 'config/environment'
run Rails.application map Rails.application.config.relative_url_root do
Rails.application.load_server run Rails.application
Rails.application.load_server
end

View File

@ -40,7 +40,9 @@ module CodeOcean
config.autoload_paths += extra_paths config.autoload_paths += extra_paths
config.eager_load_paths += extra_paths config.eager_load_paths += extra_paths
config.action_cable.mount_path = '/cable' config.relative_url_root = ENV.fetch('RAILS_RELATIVE_URL_ROOT', '/').to_s
config.action_cable.mount_path = "#{ENV.fetch('RAILS_RELATIVE_URL_ROOT', '')}/cable"
config.telegraf.tags = {application: 'codeocean'} config.telegraf.tags = {application: 'codeocean'}

View File

@ -100,7 +100,4 @@ Rails.application.configure do
# Do not dump schema after migrations. # Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false config.active_record.dump_schema_after_migration = false
# Run on subfolder in production environment.
# config.relative_url_root = '/co-staging'
end end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
JsRoutes.setup do |config|
config.documentation = false
config.prefix = Rails.application.config.relative_url_root
config.url_links = true
end

View File

@ -2,4 +2,7 @@
# Be sure to restart your server when you modify this file. # Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: '_code_ocean_session', expire_after: 1.month Rails.application.config.session_store :cookie_store,
key: '_code_ocean_session',
expire_after: 1.month,
path: Rails.application.config.relative_url_root

View File

@ -4,9 +4,10 @@ Info for this file can be found
github.com/rails/webpacker/blob/master/docs/webpack.md github.com/rails/webpacker/blob/master/docs/webpack.md
*/ */
const { environment } = require('@rails/webpacker') const {environment} = require('@rails/webpacker')
const { merge } = require('webpack-merge') const {merge} = require('webpack-merge')
const webpack = require('webpack') const webpack = require('webpack')
const erb = require('./loaders/erb')
// Add an additional plugin of your choosing : ProvidePlugin // Add an additional plugin of your choosing : ProvidePlugin
environment.plugins.prepend('Provide', new webpack.ProvidePlugin({ environment.plugins.prepend('Provide', new webpack.ProvidePlugin({
@ -24,6 +25,16 @@ environment.plugins.prepend('Provide', new webpack.ProvidePlugin({
}) })
) )
// This setting will change the absolute path used to refer
// external files (images, fonts, ...) in the generated assets
const relative_url_root = process.env.RAILS_RELATIVE_URL_ROOT || '';
const public_output_path = environment.config.output.publicPath;
environment.loaders.get('file')
.use.find(item => item.loader === 'file-loader')
.options.publicPath = relative_url_root + public_output_path;
environment.loaders.append('erb', erb)
const envConfig = module.exports = environment const envConfig = module.exports = environment
const aliasConfig = module.exports = { const aliasConfig = module.exports = {
resolve: { resolve: {

View File

@ -0,0 +1,16 @@
module.exports = {
test: /\.erb$/,
enforce: "pre",
exclude: /node_modules/,
use: [{
loader: "rails-erb-loader",
options: {
runner: (/^win/.test(process.platform) ? "ruby " : "") + "bin/rails runner",
env: {
...process.env,
DISABLE_SPRING: 1,
},
},
}],
}

View File

@ -15,6 +15,7 @@
"jstree": "^3.3.11", "jstree": "^3.3.11",
"opensans-webkit": "^1.1.0", "opensans-webkit": "^1.1.0",
"popper.js": "^1.16.1", "popper.js": "^1.16.1",
"rails-erb-loader": "^5.5.2",
"sortablejs": "^1.14.0", "sortablejs": "^1.14.0",
"underscore": "^1.13.1", "underscore": "^1.13.1",
"vis": "^4.21.0", "vis": "^4.21.0",

View File

@ -4272,6 +4272,11 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.defaults@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=
lodash.get@^4.0: lodash.get@^4.0:
version "4.4.2" version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
@ -5879,6 +5884,14 @@ querystringify@^2.1.1:
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
rails-erb-loader@^5.5.2:
version "5.5.2"
resolved "https://registry.yarnpkg.com/rails-erb-loader/-/rails-erb-loader-5.5.2.tgz#db3fa8ac89600f09d179a1a70a2ca18c592576ea"
integrity sha512-cjQH9SuSvRPhnWkvjmmAW/S4AFVDfAtYnQO4XpKJ8xpRdZayT73iXoE+IPc3VzN03noZXhVmyvsCvKvHj4LY6w==
dependencies:
loader-utils "^1.1.0"
lodash.defaults "^4.2.0"
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"