├── log └── .keep ├── tmp └── .keep ├── vendor └── .keep ├── lib └── tasks │ ├── .keep │ └── auto_annotate_models.rake ├── public ├── robots.txt ├── favicon.ico └── dummy-client.html ├── app ├── models │ ├── concerns │ │ └── .keep │ ├── application_record.rb │ ├── language.rb │ └── submission.rb ├── controllers │ ├── concerns │ │ └── .keep │ ├── statuses_controller.rb │ ├── home_controller.rb │ ├── application_controller.rb │ ├── languages_controller.rb │ ├── health_controller.rb │ ├── sessions_controller.rb │ ├── info_controller.rb │ └── submissions_controller.rb ├── views │ └── layouts │ │ ├── mailer.text.erb │ │ └── mailer.html.erb ├── jobs │ ├── application_job.rb │ └── isolate_job.rb ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── mailers │ └── application_mailer.rb ├── serializers │ ├── status_serializer.rb │ ├── language_serializer.rb │ └── submission_serializer.rb ├── helpers │ ├── nil_value.rb │ ├── system_info.rb │ └── config.rb ├── services │ ├── base64_service.rb │ └── fields │ │ └── submission.rb └── enumerations │ └── status.rb ├── .rspec ├── .github ├── FUNDING.yml ├── wallpaper.png ├── sequence-diagram.png └── sequence-diagram.txt ├── scripts ├── prod-build ├── dev-serve-api-docs ├── prod-gen-api-docs ├── env.conf ├── dev-build ├── dev-stop ├── dev-clean ├── dev-shell ├── run-server ├── load-config └── run-workers ├── config.ru ├── docs └── api │ ├── health_check │ ├── health_check.md │ └── workers.md │ ├── information │ ├── license.md │ ├── version.md │ ├── isolate.md │ ├── information.md │ └── about.md │ ├── _unauthorized.md │ ├── _unauthenticated.md │ ├── statuses_and_languages │ ├── statuses_and_languages.md │ ├── languages.md │ ├── get_a_language.md │ ├── get_statuses.md │ ├── get_languages.md │ └── get_active_and_archived_languages.md │ ├── system_and_configuration │ ├── system_and_configuration.md │ ├── system_info.md │ └── configuration_info.md │ ├── style.html │ ├── hostname.html │ ├── authorization │ └── authorization.md │ ├── authentication │ └── authentication.md │ ├── submissions │ ├── delete_a_submission.md │ ├── get_a_submission.md │ ├── get_submissions.md │ ├── submissions.md │ └── create_a_submission.md │ └── docs.md ├── config ├── environment.rb ├── boot.rb ├── cable.yml ├── initializers │ ├── mime_types.rb │ ├── application_controller_renderer.rb │ ├── resque.rb │ ├── filter_parameter_logging.rb │ ├── configuration.rb │ ├── backtrace_silencers.rb │ ├── wrap_parameters.rb │ ├── cors.rb │ ├── inflections.rb │ └── new_framework_defaults.rb ├── puma.rb ├── database.yml ├── secrets.yml ├── routes.rb ├── application.rb └── environments │ ├── development.rb │ ├── test.rb │ └── production.rb ├── spec ├── jobs │ └── isolate_job_spec.rb ├── requests │ ├── submissions_spec.rb │ └── languages_spec.rb ├── support │ ├── factory_girl.rb │ ├── shoulda_matchers.rb │ ├── database_cleaner.rb │ └── active_model_serializer_matcher.rb ├── factories │ ├── languages.rb │ └── submissions.rb ├── models │ ├── language_spec.rb │ └── submission_spec.rb ├── routing │ ├── languages_routing_spec.rb │ └── submissions_routing_spec.rb ├── controllers │ ├── statuses_controller_spec.rb │ ├── languages_controller_spec.rb │ └── submissions_controller_spec.rb ├── spec_helper.rb └── rails_helper.rb ├── .gitignore ├── bin ├── bundle ├── rake ├── rails ├── spring ├── update └── setup ├── Rakefile ├── db ├── migrate │ ├── 20170203021217_add_index_to_submission.rb │ ├── 20160905030833_add_time_to_submission.rb │ ├── 20170201000546_add_token_to_submission.rb │ ├── 20160907052244_add_memory_to_submission.rb │ ├── 20170131233631_add_stderr_to_submissions.rb │ ├── 20200115205044_add_archive_to_submissions.rb │ ├── 20160905015919_add_source_file_to_language.rb │ ├── 20170928174721_add_wall_time_to_submissions.rb │ ├── 20200114220437_add_callback_url_to_submissions.rb │ ├── 20170203023903_add_number_of_runs_to_submission.rb │ ├── 20170919210318_add_compile_output_to_submissions.rb │ ├── 20170929175654_rename_input_to_stdin_in_submissions.rb │ ├── 20191230001624_add_is_archived_to_languages.rb │ ├── 20190921115544_add_compiler_options_to_submission.rb │ ├── 20190921193416_add_command_line_arguments_to_submission.rb │ ├── 20170131231840_rename_actual_output_in_submissions_to_stdout.rb │ ├── 20200113231131_add_redirect_stderr_to_stdout_to_submission.rb │ ├── 20160903140859_create_languages.rb │ ├── 20170919215014_add_exit_code_exit_signal_and_message_to_submissions.rb │ ├── 20160903211542_create_submissions.rb │ ├── 20170422122148_change_encode_all_submissions.rb │ └── 20170203215830_add_configuration_to_submission.rb ├── seeds.rb ├── schema.rb └── languages │ ├── active.rb │ └── archived.rb ├── .dockerignore ├── Dockerfile.dev ├── Gemfile ├── docker-compose.yml ├── Dockerfile ├── docker-compose.dev.yml ├── RELEASE_NOTES_TEMPLATE.md ├── README.md ├── Gemfile.lock └── judge0-api.conf.default /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: hermanzdosilovic 2 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /scripts/prod-build: -------------------------------------------------------------------------------- 1 | docker build -t judge0/api . 2 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require_relative 'config/environment' 2 | 3 | run Rails.application 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CrazyBoyM/api/master/public/favicon.ico -------------------------------------------------------------------------------- /.github/wallpaper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CrazyBoyM/api/master/.github/wallpaper.png -------------------------------------------------------------------------------- /docs/api/health_check/health_check.md: -------------------------------------------------------------------------------- 1 | # Group Health Check 2 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | require_relative 'application' 2 | 3 | Rails.application.initialize! 4 | -------------------------------------------------------------------------------- /docs/api/information/license.md: -------------------------------------------------------------------------------- 1 | ## License [/license] 2 | ## License [GET] 3 | Returns a license. -------------------------------------------------------------------------------- /scripts/dev-serve-api-docs: -------------------------------------------------------------------------------- 1 | aglio --theme-full-width -i docs/api/docs.md -s -h 0.0.0.0 -p 3030 2 | -------------------------------------------------------------------------------- /scripts/prod-gen-api-docs: -------------------------------------------------------------------------------- 1 | aglio --theme-full-width -i docs/api/docs.md -o public/docs.html 2 | -------------------------------------------------------------------------------- /docs/api/information/version.md: -------------------------------------------------------------------------------- 1 | ## Version [/version] 2 | ## Version [GET] 3 | Returns current version. -------------------------------------------------------------------------------- /.github/sequence-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CrazyBoyM/api/master/.github/sequence-diagram.png -------------------------------------------------------------------------------- /scripts/env.conf: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export JUDGE0_DEV_USER=$(whoami) 3 | export JUDGE0_DEV_UID=$(id -u) 4 | -------------------------------------------------------------------------------- /scripts/dev-build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source scripts/env.conf 3 | docker-compose -f docker-compose.dev.yml build 4 | -------------------------------------------------------------------------------- /scripts/dev-stop: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source scripts/env.conf 3 | docker-compose -f docker-compose.dev.yml stop 4 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' 4 | -------------------------------------------------------------------------------- /spec/jobs/isolate_job_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe IsolateJob, type: :job do 4 | end 5 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /spec/requests/submissions_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Submissions", type: :request do 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /docs/api/_unauthorized.md: -------------------------------------------------------------------------------- 1 | + Response 403 2 | Authorization failed. Please read about [authorization](#authorization) process. 3 | + Body -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle 2 | /log/* 3 | /tmp/* 4 | srv 5 | !/log/.keep 6 | !/tmp/.keep 7 | .byebug_history 8 | coverage/ 9 | judge0-api.conf 10 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /docs/api/_unauthenticated.md: -------------------------------------------------------------------------------- 1 | + Response 401 2 | Authentication failed. Please read about [authentication](#authentication) process. 3 | + Body -------------------------------------------------------------------------------- /docs/api/information/isolate.md: -------------------------------------------------------------------------------- 1 | ## Isolate [/isolate] 2 | ## Isolate [GET] 3 | Returns result of [`isolate --version`](https://github.com/ioi/isolate). -------------------------------------------------------------------------------- /spec/support/factory_girl.rb: -------------------------------------------------------------------------------- 1 | require 'factory_girl_rails' 2 | 3 | RSpec.configure do |config| 4 | config.include FactoryGirl::Syntax::Methods 5 | end 6 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require_relative 'config/application' 2 | require 'resque/tasks' 3 | 4 | task 'resque:setup' => :environment 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /docs/api/statuses_and_languages/statuses_and_languages.md: -------------------------------------------------------------------------------- 1 | # Group Statuses and Languages 2 | 3 | -------------------------------------------------------------------------------- /scripts/dev-clean: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source scripts/env.conf 3 | docker-compose -f docker-compose.dev.yml down 4 | docker-compose -f docker-compose.dev.yml down -v -------------------------------------------------------------------------------- /app/serializers/status_serializer.rb: -------------------------------------------------------------------------------- 1 | class StatusSerializer < ActiveModel::Serializer 2 | attribute(:id) { object.id } 3 | attribute(:description) { object.name } 4 | end 5 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | -------------------------------------------------------------------------------- /docs/api/system_and_configuration/system_and_configuration.md: -------------------------------------------------------------------------------- 1 | # Group System and Configuration 2 | 3 | -------------------------------------------------------------------------------- /app/helpers/nil_value.rb: -------------------------------------------------------------------------------- 1 | module NilValue 2 | def self.value_or_default(value, default) 3 | return value unless value == nil 4 | return default 5 | end 6 | end -------------------------------------------------------------------------------- /app/serializers/language_serializer.rb: -------------------------------------------------------------------------------- 1 | class LanguageSerializer < ActiveModel::Serializer 2 | attributes :id, :name, :is_archived, :source_file, :compile_cmd, :run_cmd 3 | end 4 | -------------------------------------------------------------------------------- /db/migrate/20170203021217_add_index_to_submission.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToSubmission < ActiveRecord::Migration[5.0] 2 | def change 3 | add_index :submissions, :token 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /docs/api/information/information.md: -------------------------------------------------------------------------------- 1 | # Group Information 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /scripts/dev-shell: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source scripts/env.conf 3 | docker-compose -f docker-compose.dev.yml up -d 4 | docker-compose -f docker-compose.dev.yml exec --privileged api bash 5 | -------------------------------------------------------------------------------- /app/controllers/statuses_controller.rb: -------------------------------------------------------------------------------- 1 | class StatusesController < ApplicationController 2 | def index 3 | render json: Status.all, each_serializer: StatusSerializer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160905030833_add_time_to_submission.rb: -------------------------------------------------------------------------------- 1 | class AddTimeToSubmission < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :time, :decimal 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170201000546_add_token_to_submission.rb: -------------------------------------------------------------------------------- 1 | class AddTokenToSubmission < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :token, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160907052244_add_memory_to_submission.rb: -------------------------------------------------------------------------------- 1 | class AddMemoryToSubmission < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :memory, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170131233631_add_stderr_to_submissions.rb: -------------------------------------------------------------------------------- 1 | class AddStderrToSubmissions < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :stderr, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/languages.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :language do 3 | sequence(:name) { |n| "C (gcc #{n})" } 4 | run_cmd './a.out' 5 | source_file 'main.c' 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /db/migrate/20200115205044_add_archive_to_submissions.rb: -------------------------------------------------------------------------------- 1 | class AddArchiveToSubmissions < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :archive, :binary 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ActionController::API 2 | include ActionView::Layouts 3 | 4 | def docs 5 | render file: Rails.root.join('public/docs.html') 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20160905015919_add_source_file_to_language.rb: -------------------------------------------------------------------------------- 1 | class AddSourceFileToLanguage < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :languages, :source_file, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170928174721_add_wall_time_to_submissions.rb: -------------------------------------------------------------------------------- 1 | class AddWallTimeToSubmissions < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :wall_time, :decimal 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /docs/api/statuses_and_languages/languages.md: -------------------------------------------------------------------------------- 1 | ## Language [/languages/{id}] 2 | 3 | 4 | -------------------------------------------------------------------------------- /db/migrate/20200114220437_add_callback_url_to_submissions.rb: -------------------------------------------------------------------------------- 1 | class AddCallbackUrlToSubmissions < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :callback_url, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170203023903_add_number_of_runs_to_submission.rb: -------------------------------------------------------------------------------- 1 | class AddNumberOfRunsToSubmission < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :number_of_runs, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170919210318_add_compile_output_to_submissions.rb: -------------------------------------------------------------------------------- 1 | class AddCompileOutputToSubmissions < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :compile_output, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170929175654_rename_input_to_stdin_in_submissions.rb: -------------------------------------------------------------------------------- 1 | class RenameInputToStdinInSubmissions < ActiveRecord::Migration[5.0] 2 | def change 3 | rename_column :submissions, :input, :stdin 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191230001624_add_is_archived_to_languages.rb: -------------------------------------------------------------------------------- 1 | class AddIsArchivedToLanguages < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :languages, :is_archived, :bool, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/shoulda_matchers.rb: -------------------------------------------------------------------------------- 1 | require 'shoulda-matchers' 2 | 3 | Shoulda::Matchers.configure do |config| 4 | config.integrate do |with| 5 | with.test_framework :rspec 6 | with.library :rails 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20190921115544_add_compiler_options_to_submission.rb: -------------------------------------------------------------------------------- 1 | class AddCompilerOptionsToSubmission < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :compiler_options, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .* 2 | CHANGELOG.md 3 | Dockerfile* 4 | README.md 5 | docker-compose* 6 | judge0-api.conf* 7 | log/* 8 | scripts 9 | !scripts/load-config 10 | !scripts/run-server 11 | !scripts/run-workers 12 | srv 13 | tmp/* 14 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /config/initializers/resque.rb: -------------------------------------------------------------------------------- 1 | Resque.redis = Redis.new( 2 | host: ENV["REDIS_HOST"].presence || "localhost", 3 | port: ENV["REDIS_PORT"].presence || 6379, 4 | password: ENV["REDIS_PASSWORD"], 5 | thread_safe: true 6 | ) 7 | -------------------------------------------------------------------------------- /db/migrate/20190921193416_add_command_line_arguments_to_submission.rb: -------------------------------------------------------------------------------- 1 | class AddCommandLineArgumentsToSubmission < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :command_line_arguments, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | threads_count = (ENV['RAILS_MAX_THREADS'].presence || '5').to_i 2 | threads threads_count, threads_count 3 | port (ENV['PORT'].presence || '3000') 4 | environment (ENV['RAILS_ENV'].presence || 'production') 5 | 6 | plugin :tmp_restart -------------------------------------------------------------------------------- /db/migrate/20170131231840_rename_actual_output_in_submissions_to_stdout.rb: -------------------------------------------------------------------------------- 1 | class RenameActualOutputInSubmissionsToStdout < ActiveRecord::Migration[5.0] 2 | def change 3 | rename_column :submissions, :actual_output, :stdout 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /db/migrate/20200113231131_add_redirect_stderr_to_stdout_to_submission.rb: -------------------------------------------------------------------------------- 1 | class AddRedirectStderrToStdoutToSubmission < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :redirect_stderr_to_stdout, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /spec/models/language_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Language, type: :model do 4 | it { should validate_presence_of(:name) } 5 | it { should validate_presence_of(:run_cmd) } 6 | it { should validate_presence_of(:source_file) } 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20160903140859_create_languages.rb: -------------------------------------------------------------------------------- 1 | class CreateLanguages < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :languages do |t| 4 | t.string :name 5 | t.string :compile_cmd 6 | t.string :run_cmd 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/routing/languages_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe LanguagesController, type: :routing do 4 | describe "routing" do 5 | it "routes to #index" do 6 | expect(get: "/languages").to route_to("languages#index") 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /scripts/run-server: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source ./scripts/load-config 3 | 4 | [[ -f tmp/pids/server.pid ]] && rm -f tmp/pids/server.pid 5 | 6 | export DISABLE_DATABASE_ENVIRONMENT_CHECK=1 7 | export RUBYOPT=-W:no-deprecated 8 | 9 | rails db:create db:migrate db:seed 10 | rails s -b 0.0.0.0 11 | -------------------------------------------------------------------------------- /spec/requests/languages_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Languages", type: :request do 4 | describe "GET /languages" do 5 | it "has a 200 status code" do 6 | get languages_path 7 | expect(response).to have_http_status(200) 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/services/base64_service.rb: -------------------------------------------------------------------------------- 1 | module Base64Service 2 | def self.encode(text) 3 | return nil unless text 4 | Base64.encode64(text) 5 | end 6 | 7 | def self.decode(text) 8 | return nil unless text 9 | Base64.decode64(text) #.force_encoding("UTF-8").encode 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /config/initializers/configuration.rb: -------------------------------------------------------------------------------- 1 | ENV['INTERVAL'] = ENV['INTERVAL'].presence || '0.1' 2 | ENV['COUNT'] = ENV['COUNT'].presence || '1' 3 | ENV['QUEUE'] = ENV['QUEUE'].presence || '*' 4 | ENV['RAILS_ENV'] = ENV['RAILS_ENV'].presence || 'production' 5 | ENV['POSTGRES_DB'] = ENV['POSTGRES_DB'].presence || 'postgres' -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /scripts/load-config: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [[ -f judge0-api.conf ]]; then 3 | CONFIG_FILE=judge0-api.conf 4 | elif [[ -f /judge0-api.conf ]]; then 5 | CONFIG_FILE=/judge0-api.conf 6 | fi 7 | 8 | if [[ -v CONFIG_FILE ]]; then 9 | set -o allexport 10 | source $CONFIG_FILE 11 | set +o allexport 12 | fi 13 | -------------------------------------------------------------------------------- /db/migrate/20170919215014_add_exit_code_exit_signal_and_message_to_submissions.rb: -------------------------------------------------------------------------------- 1 | class AddExitCodeExitSignalAndMessageToSubmissions < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :exit_code, :integer 4 | add_column :submissions, :exit_signal, :integer 5 | add_column :submissions, :message, :text 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM judge0/api 2 | 3 | ARG DEV_USER=judge0 4 | ARG DEV_USER_ID=1000 5 | 6 | RUN apt-get update && \ 7 | apt-get install -y --no-install-recommends vim && \ 8 | useradd -u $DEV_USER_ID -m -r $DEV_USER && \ 9 | echo "$DEV_USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers 10 | 11 | USER $DEV_USER 12 | 13 | CMD ["sleep", "infinity"] 14 | -------------------------------------------------------------------------------- /spec/controllers/statuses_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe StatusesController, type: :controller do 4 | describe "GET #index" do 5 | it "returns all statuses" do 6 | get :index 7 | json = JSON.parse(response.body) 8 | expect(response).to be_success 9 | expect(json).to have_serialized(Status.all).with(StatusSerializer) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < SessionsController 2 | private 3 | 4 | def pagination_dict(collection) 5 | { 6 | current_page: collection.current_page, 7 | next_page: collection.next_page, 8 | prev_page: collection.previous_page, 9 | total_pages: collection.total_pages, 10 | total_count: collection.total_entries 11 | } 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /docs/api/information/about.md: -------------------------------------------------------------------------------- 1 | ## About [/about] 2 | ## About [GET] 3 | Returns general information. 4 | 5 | + Response 200 (application/json) 6 | + Body 7 | { 8 | "version": "1.5.0", 9 | "homepage": "https://judge0.com", 10 | "source_code": "https://github.com/judge0/api", 11 | "maintainer": "Herman Zvonimir Došilović " 12 | } -------------------------------------------------------------------------------- /db/migrate/20160903211542_create_submissions.rb: -------------------------------------------------------------------------------- 1 | class CreateSubmissions < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :submissions do |t| 4 | t.text :source_code 5 | t.integer :language_id 6 | t.text :input 7 | t.text :expected_output 8 | t.text :actual_output 9 | t.integer :status_id 10 | t.datetime :created_at 11 | t.datetime :finished_at 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /spec/controllers/languages_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe LanguagesController, type: :controller do 4 | describe "GET #index" do 5 | it "returns all languages" do 6 | create_list(:language, 10) 7 | get :index 8 | json = JSON.parse(response.body) 9 | expect(response).to be_success 10 | expect(json).to have_serialized(Language.all).with(LanguageSerializer) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/languages_controller.rb: -------------------------------------------------------------------------------- 1 | class LanguagesController < ApplicationController 2 | def index 3 | render json: Language.all, each_serializer: LanguageSerializer, fields: [:id, :name] 4 | end 5 | 6 | def all 7 | render json: Language.unscoped.order(name: :asc), each_serializer: LanguageSerializer, fields: [:id, :name, :is_archived] 8 | end 9 | 10 | def show 11 | render json: Language.unscoped.find(params[:id]) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /docs/api/style.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/api/statuses_and_languages/get_a_language.md: -------------------------------------------------------------------------------- 1 | ## Get a Language [GET] 2 | + Parameters 3 | + id (required, integer, `1`) ... Language ID. 4 | 5 | + Response 200 (application/json) 6 | + Body 7 | { 8 | "id": 1, 9 | "name": "Bash (4.4)", 10 | "is_archived": true, 11 | "source_file": "script.sh", 12 | "compile_cmd": null, 13 | "run_cmd": "/usr/local/bash-4.4/bin/bash script.sh" 14 | } -------------------------------------------------------------------------------- /spec/routing/submissions_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe SubmissionsController, type: :routing do 4 | describe "routing" do 5 | it "routes to #show" do 6 | token = SecureRandom.uuid 7 | expect(:get => "/submissions/#{token}").to route_to("submissions#show", token: token) 8 | end 9 | 10 | it "routes to #create" do 11 | expect(:post => "/submissions").to route_to("submissions#create") 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/models/language.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: languages 4 | # 5 | # id :integer not null, primary key 6 | # name :string 7 | # compile_cmd :string 8 | # run_cmd :string 9 | # source_file :string 10 | # is_archived :boolean default(FALSE) 11 | # 12 | 13 | class Language < ApplicationRecord 14 | validates :name, :run_cmd, :source_file, presence: true 15 | default_scope { where(is_archived: false).order(name: :asc) } 16 | end 17 | -------------------------------------------------------------------------------- /spec/support/database_cleaner.rb: -------------------------------------------------------------------------------- 1 | require 'database_cleaner' 2 | 3 | RSpec.configure do |config| 4 | config.use_transactional_fixtures = false 5 | 6 | config.before(:suite) do 7 | DatabaseCleaner.clean_with(:truncation) 8 | end 9 | 10 | config.before(:each) do 11 | DatabaseCleaner.strategy = :transaction 12 | end 13 | 14 | config.before(:each) do 15 | DatabaseCleaner.start 16 | end 17 | 18 | config.after(:each) do 19 | DatabaseCleaner.clean 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '~> 5.0.0' 4 | gem 'pg', '~> 1.2.2' 5 | 6 | gem 'active_model_serializers', '~> 0.10.10' 7 | gem 'enumerations', '~> 2.3.3' 8 | gem 'httparty', '~> 0.17.3' 9 | gem 'pry-byebug', '~> 3.7.0' 10 | gem 'pry-rails', '~> 0.3.9' 11 | gem 'puma', '~> 4.3.1' 12 | gem 'rack-cors', '~> 1.1.1' 13 | gem 'resque', '~> 2.0.0' 14 | gem 'will_paginate', '~> 3.2.1' 15 | 16 | group :development do 17 | gem 'annotate', '~> 3.0.3' 18 | gem 'listen', '~> 3.2.1' 19 | end -------------------------------------------------------------------------------- /db/migrate/20170422122148_change_encode_all_submissions.rb: -------------------------------------------------------------------------------- 1 | class ChangeEncodeAllSubmissions < ActiveRecord::Migration[5.0] 2 | def change 3 | Submission.all.each do |submission| 4 | submission.source_code = submission[:source_code] 5 | submission.input = submission[:input] 6 | submission.stdout = submission[:stdout] 7 | submission.expected_output = submission[:expected_output] 8 | submission.stderr = submission[:stderr] 9 | submission.save 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | require_relative 'languages/archived' 2 | require_relative 'languages/active' 3 | 4 | ActiveRecord::Base.transaction do 5 | Language.unscoped.delete_all 6 | @languages.each_with_index do |language, index| 7 | Language.create( 8 | id: language[:id], 9 | name: language[:name], 10 | is_archived: language[:is_archived], 11 | source_file: language[:source_file], 12 | compile_cmd: language[:compile_cmd], 13 | run_cmd: language[:run_cmd], 14 | ) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'codeclimate-test-reporter' 2 | 3 | CodeClimate::TestReporter.start 4 | CodeClimate::TestReporter.configuration.git_dir = ".." 5 | 6 | RSpec.configure do |config| 7 | config.expect_with :rspec do |expectations| 8 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 9 | end 10 | 11 | config.mock_with :rspec do |mocks| 12 | mocks.verify_partial_doubles = true 13 | end 14 | 15 | config.shared_context_metadata_behavior = :apply_to_host_groups 16 | end 17 | -------------------------------------------------------------------------------- /app/helpers/system_info.rb: -------------------------------------------------------------------------------- 1 | module SystemInfo 2 | def self.sys_info 3 | @@sys_info ||= self.cpu_info.merge(self.mem_info) 4 | end 5 | 6 | def self.cpu_info #sorryforthisline 7 | @@cpu_info ||= Hash[`lscpu`.split("\n").collect{|l| l = l.split(":"); [l[0].strip, l[1].strip]}] 8 | end 9 | 10 | def self.mem_info #sorryagain #itjustworks 11 | @@mem_info ||= Hash[`free -h`.split("\n")[1..-1].collect{|l| l = l.split(":"); [l[0].strip, l[1].split(" ")[0].strip]}].without("-/+ buffers/cache") 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) 11 | Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) } 12 | gem 'spring', match[1] 13 | require 'spring/binstub' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: postgresql 3 | encoding: unicode 4 | host: <%= ENV["POSTGRES_HOST"].presence || "localhost" %> 5 | port: <%= ENV["POSTGRES_PORT"].presence || 5432 %> 6 | database: <%= ENV["POSTGRES_DB"].presence || "postgres" %> 7 | username: <%= ENV["POSTGRES_USER"].presence || "postgres" %> 8 | password: <%= ENV["POSTGRES_PASSWORD"] %> 9 | pool: <%= ENV["RAILS_MAX_THREADS"].presence || 5 %> 10 | 11 | development: 12 | <<: *default 13 | 14 | test: 15 | <<: *default 16 | 17 | production: 18 | <<: *default 19 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /docs/api/hostname.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Avoid CORS issues when API is called from the frontend app. 4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. 5 | 6 | # Read more: https://github.com/cyu/rack-cors 7 | 8 | # Rails.application.config.middleware.insert_before 0, Rack::Cors do 9 | # allow do 10 | # origins 'example.com' 11 | # 12 | # resource '*', 13 | # headers: :any, 14 | # methods: [:get, :post, :put, :patch, :delete, :options, :head] 15 | # end 16 | # end 17 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] = 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | abort("The Rails environment is running in production mode!") if Rails.env.production? 4 | require 'spec_helper' 5 | require 'rspec/rails' 6 | require 'pry-byebug' 7 | 8 | Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } 9 | 10 | ActiveRecord::Migration.maintain_test_schema! 11 | 12 | RSpec.configure do |config| 13 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 14 | config.infer_spec_type_from_file_location! 15 | config.filter_rails_from_backtrace! 16 | end 17 | -------------------------------------------------------------------------------- /scripts/run-workers: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source ./scripts/load-config 3 | 4 | export RUBYOPT=-W:no-deprecated 5 | 6 | run_resque=1 7 | resque_pid=0 8 | 9 | exit_gracefully() { 10 | run_resque=0 11 | kill -SIGQUIT $(pgrep -P $resque_pid) 12 | kill -SIGTERM $resque_pid 13 | } 14 | 15 | trap exit_gracefully SIGTERM SIGINT 16 | 17 | mkdir -p tmp/pids &> /dev/null 18 | while [[ $run_resque -eq 1 ]]; do 19 | rm -rf tmp/pids/resque.pid &> /dev/null 20 | COUNT=${COUNT:-1} QUEUE=$JUDGE0_VERSION rails resque:workers & 21 | resque_pid=$! 22 | while ps -p $resque_pid > /dev/null; do sleep 1s; done 23 | done 24 | -------------------------------------------------------------------------------- /.github/sequence-diagram.txt: -------------------------------------------------------------------------------- 1 | # https://bramp.github.io/js-sequence-diagrams/ 2 | User->Rails API: createNewSubmission(source_code, language_id, input, expected_output) 3 | Rails API->PostgreSQL: Submission := createSubmission(source_code, language_id, input, expected_output) 4 | PostgreSQL-->>Rails API: created 5 | Rails API->>Redis: createNewJob(submission) 6 | Rails API-->>User: created(id) 7 | Worker->Redis: * IsolateJob := getNextJob() 8 | Worker->Worker: runInSandbox(submission.source_code) 9 | Worker->Worker: determineStatus() 10 | Worker->PostgreSQL: updateSubmission(submission) 11 | User->Rails API: getSubmission(id) 12 | Rails API->PostgreSQL: Submission := findSubmission(id) 13 | PostgreSQL-->>Rails API: 14 | Rails API-->>User: 15 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /app/controllers/health_controller.rb: -------------------------------------------------------------------------------- 1 | class HealthController < ApplicationController 2 | def workers 3 | queues = Hash.new { |h, k| h[k] = [] } 4 | Resque.workers.each do |worker| 5 | worker.queues.each do |queue| 6 | queues[queue] << worker 7 | end 8 | end 9 | 10 | json = [] 11 | Resque.queues.each do |queue| 12 | workers = queues[queue] 13 | json << { 14 | queue: queue, 15 | available: workers.count, 16 | idle: workers.count { |w| w.idle? }, 17 | working: workers.count { |w| w.working? }, 18 | paused: workers.count { |w| w.paused? }, 19 | failed: workers.sum { |w| w.failed } 20 | } 21 | end 22 | 23 | render json: json 24 | end 25 | end -------------------------------------------------------------------------------- /db/migrate/20170203215830_add_configuration_to_submission.rb: -------------------------------------------------------------------------------- 1 | class AddConfigurationToSubmission < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :submissions, :cpu_time_limit, :decimal 4 | add_column :submissions, :cpu_extra_time, :decimal 5 | add_column :submissions, :wall_time_limit, :decimal 6 | add_column :submissions, :memory_limit, :integer 7 | add_column :submissions, :stack_limit, :integer 8 | add_column :submissions, :max_processes_and_or_threads, :integer 9 | add_column :submissions, :enable_per_process_and_thread_time_limit, :boolean 10 | add_column :submissions, :enable_per_process_and_thread_memory_limit, :boolean 11 | add_column :submissions, :max_file_size, :integer 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/factories/submissions.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :valid_submission, class: Submission do 3 | sequence(:token) { |n| SecureRandom.uuid + "-#{n}" } 4 | source_code 'name = gets.strip; puts "hello, " + name' 5 | language_id { create(:language).id } 6 | number_of_runs 1 7 | stdin "world" 8 | expected_output "hello, world" 9 | end 10 | 11 | factory :submission, parent: :valid_submission do 12 | stdout "hello, world" 13 | status_id 1 14 | time 1.0 15 | memory 256 16 | end 17 | 18 | factory :invalid_submission, class: Submission do 19 | source_code 'name = gets.strip; puts "hello, " + name' 20 | # language_id 14 # :language_id should be present 21 | stdin "world" 22 | expected_output "hello, world" 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | authn_header: <%= ENV["AUTHN_HEADER"].presence || "X-Auth-Token" %> 3 | authn_token: <%= ENV["AUTHN_TOKEN"].to_s.strip %> 4 | authz_header: <%= ENV["AUTHZ_HEADER"].presence || "X-Auth-User" %> 5 | authz_token: <%= ENV["AUTHZ_TOKEN"].to_s.strip %> 6 | 7 | development: 8 | <<: *default 9 | secret_key_base: 54663e3c477de43bd117ddbf1798951ba5cf692dbf8327bb21f341048f04ea95344eed6c8763625a39648bfc2583f128b296bcb3a4756ef8160e483664efcb0b 10 | 11 | test: 12 | secret_key_base: 334f81f95f09f01e58b950ff5e5f9cd914cc151c186358d509d44520d4a1fb46c5af2dea39ed06a897c7cfe58b9c93316fa6c81081b7a30f5e43d271ca2aec0d 13 | 14 | production: 15 | <<: *default 16 | secret_key_base: <%= ENV["SECRET_KEY_BASE"].presence || (0...128).map { [*'0'..'9', *'a'..'z', *'A'..'Z'].sample }.join %> 17 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | api: 4 | image: judge0/api 5 | volumes: 6 | - ./judge0-api.conf:/judge0-api.conf:ro 7 | ports: 8 | - "3000:3000" 9 | privileged: true 10 | restart: always 11 | 12 | worker: 13 | image: judge0/api 14 | command: ["./scripts/run-workers"] 15 | volumes: 16 | - ./judge0-api.conf:/judge0-api.conf:ro 17 | privileged: true 18 | restart: always 19 | 20 | db: 21 | image: postgres:9.6 22 | env_file: judge0-api.conf 23 | volumes: 24 | - ./srv/postgres-data:/var/lib/postgresql/data/ 25 | restart: always 26 | 27 | redis: 28 | image: redis:4.0 29 | command: [ 30 | "bash", "-c", 31 | 'docker-entrypoint.sh --appendonly yes --requirepass "$$REDIS_PASSWORD"' 32 | ] 33 | env_file: judge0-api.conf 34 | volumes: 35 | - ./srv/redis-data:/data 36 | restart: always 37 | -------------------------------------------------------------------------------- /spec/models/submission_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Submission, type: :model do 4 | it { should validate_presence_of(:source_code) } 5 | it { should validate_presence_of(:language_id) } 6 | 7 | describe "#language" do 8 | let(:language) { create(:language) } 9 | 10 | it "returns language" do 11 | submission = create(:submission, { language_id: language.id }) 12 | expect(submission.language).to eq(language) 13 | end 14 | end 15 | 16 | it "is invalid because Language with given id doesn't exist" do 17 | submission = build(:submission, { language_id: Language.all.size + 1 }) 18 | expect(submission).not_to be_valid 19 | end 20 | 21 | it "is valid because Language with given id exists" do 22 | language = create(:language) 23 | submission = build(:submission, { language_id: language.id }) 24 | expect(submission).to be_valid 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.0 upgrade. 4 | # 5 | # Read the Rails 5.0 release notes for more info on each option. 6 | 7 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 8 | # Previous versions had false. 9 | ActiveSupport.to_time_preserves_timezone = true 10 | 11 | # Require `belongs_to` associations by default. Previous versions had false. 12 | Rails.application.config.active_record.belongs_to_required_by_default = true 13 | 14 | # Do not halt callback chains when a callback returns false. Previous versions had true. 15 | ActiveSupport.halt_callback_chains_on_return_false = false 16 | 17 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 18 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 19 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root 'home#docs' 3 | 4 | resources :submissions, only: [:index, :show, :create, :destroy], param: :token do 5 | post 'batch', to: 'submissions#batch_create', on: :collection 6 | get 'batch', to: 'submissions#batch_show', on: :collection 7 | end 8 | 9 | resources :languages, only: [:index, :show] do 10 | get 'all', to: 'languages#all', on: :collection 11 | end 12 | 13 | resources :statuses, only: [:index] 14 | 15 | get 'system_info', to: 'info#system_info' 16 | get 'config_info', to: 'info#config_info' 17 | get 'isolate', to: 'info#isolate' 18 | get 'about', to: 'info#about' 19 | get 'version', to: 'info#version' 20 | get 'license', to: 'info#license' 21 | get 'statistics', to: 'info#statistics' 22 | 23 | post 'authenticate', to: 'sessions#authenticate' 24 | post 'authorize', to: 'sessions#authorize' 25 | 26 | get 'workers', to: 'health#workers' 27 | end 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM judge0/api-base:1.0.0 2 | 3 | ENV JUDGE0_HOMEPAGE="https://judge0.com" 4 | LABEL homepage=$JUDGE0_HOMEPAGE 5 | 6 | ENV JUDGE0_SOURCE_CODE="https://github.com/judge0/api" 7 | LABEL source_code=$JUDGE0_SOURCE_CODE 8 | 9 | ENV JUDGE0_MAINTAINER="Herman Zvonimir Došilović " 10 | LABEL maintainer=$JUDGE0_MAINTAINER 11 | 12 | ENV PATH "/usr/local/ruby-2.7.0/bin:/opt/.gem/bin:$PATH" 13 | ENV GEM_HOME "/opt/.gem/" 14 | 15 | RUN apt-get update && \ 16 | apt-get install -y --no-install-recommends \ 17 | libpq-dev \ 18 | sudo && \ 19 | rm -rf /var/lib/apt/lists/* && \ 20 | echo "gem: --no-document" > /root/.gemrc && \ 21 | gem install bundler:2.1.4 && \ 22 | npm install -g --unsafe-perm aglio@2.3.0 23 | 24 | EXPOSE 3000 25 | 26 | WORKDIR /api 27 | 28 | COPY Gemfile* ./ 29 | RUN RAILS_ENV=production bundle 30 | 31 | COPY . . 32 | 33 | CMD ["./scripts/run-server"] 34 | 35 | ENV JUDGE0_VERSION="1.5.0" 36 | LABEL version=$JUDGE0_VERSION 37 | -------------------------------------------------------------------------------- /app/services/fields/submission.rb: -------------------------------------------------------------------------------- 1 | module Fields 2 | class Submission 3 | UNIVERSAL_FIELD = :* 4 | 5 | attr_reader :requested_fields, :invalid_fields 6 | 7 | def initialize(fields) 8 | @invalid_fields = [] 9 | 10 | fields = fields.to_s.split(",").collect(&:to_sym) 11 | 12 | fields.each do |field| 13 | if field != UNIVERSAL_FIELD && !available_fields.include?(field) 14 | @invalid_fields << field 15 | end 16 | end 17 | 18 | if fields.include?(UNIVERSAL_FIELD) 19 | @requested_fields = available_fields 20 | else 21 | @requested_fields = (fields - @invalid_fields).presence || default_fields 22 | end 23 | end 24 | 25 | def has_invalid_fields? 26 | !@invalid_fields.empty? 27 | end 28 | 29 | def available_fields 30 | serializer._attributes 31 | end 32 | 33 | def default_fields 34 | serializer.default_fields 35 | end 36 | 37 | def serializer 38 | SubmissionSerializer 39 | end 40 | end 41 | end -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /docs/api/health_check/workers.md: -------------------------------------------------------------------------------- 1 | ## Workers Health Check [/workers] 2 | ### Workers Health Check [GET] 3 | For each queue you will get: 4 | - `queue` name 5 | - `total` number of workers that has been initially run 6 | - `available` number of workers 7 | - how many workers are `idle` 8 | - how many workers are currently `working` 9 | - how many workers are `paused` 10 | - how many jobs `failed` 11 | 12 | + Response 200 (application/json) 13 | [ 14 | { 15 | "queue": "default", 16 | "total": 1, 17 | "available": 1, 18 | "idle": 1, 19 | "working": 0, 20 | "paused": 0, 21 | "failed": 0 22 | } 23 | ] 24 | 25 | + Response 500 (application/json) 26 | If `total` is not equal to `available`. This means that some workers died. 27 | + Body 28 | [ 29 | { 30 | "queue": "default", 31 | "total": 3, 32 | "available": 1, 33 | "idle": 1, 34 | "working": 0, 35 | "paused": 0, 36 | "failed": 0 37 | } 38 | ] -------------------------------------------------------------------------------- /app/enumerations/status.rb: -------------------------------------------------------------------------------- 1 | class Status < Enumerations::Base 2 | values queue: { id: 1, name: 'In Queue' }, 3 | process: { id: 2, name: 'Processing' }, 4 | ac: { id: 3, name: 'Accepted' }, 5 | wa: { id: 4, name: 'Wrong Answer' }, 6 | tle: { id: 5, name: 'Time Limit Exceeded' }, 7 | ce: { id: 6, name: 'Compilation Error' }, 8 | sigsegv: { id: 7, name: 'Runtime Error (SIGSEGV)' }, 9 | sigxfsz: { id: 8, name: 'Runtime Error (SIGXFSZ)' }, 10 | sigfpe: { id: 9, name: 'Runtime Error (SIGFPE)' }, 11 | sigabrt: { id: 10, name: 'Runtime Error (SIGABRT)' }, 12 | nzec: { id: 11, name: 'Runtime Error (NZEC)' }, 13 | other: { id: 12, name: 'Runtime Error (Other)' }, 14 | boxerr: { id: 13, name: 'Internal Error' }, 15 | exeerr: { id: 14, name: 'Exec Format Error' } 16 | 17 | def self.find_runtime_error_by_status_code(status_code) 18 | case status_code.to_i 19 | when 11 then Status.sigsegv 20 | when 25 then Status.sigxfsz 21 | when 8 then Status.sigfpe 22 | when 6 then Status.sigabrt 23 | else Status.other 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /docs/api/authorization/authorization.md: -------------------------------------------------------------------------------- 1 | # Group Authorization 2 | To issue some API calls you need to be authorized. For example, you need to be authorized to 3 | [list all submissions](#submissions-submission-get-1) on Judge0 API. 4 | 5 | ## Authorize [/authorize{?X-Auth-User}] 6 | ### Authorize [POST] 7 | With this API call you can check if your authorization token is valid. If authentication is enabled you should also 8 | authenticate in this API call. 9 | 10 | ::: note 11 |

Note

12 | * `X-Auth-User` is default header field name, but administrators of Judge0 API instance you are using 13 | can change this default field name. 14 | * Contact administrator of Judge0 API instance you are using to get your authorization token. 15 | ::: 16 | 17 | ::: warning 18 |

Security Warning

19 | * Although you can send authorization token as URI parameter, **always** send authorization token through headers. 20 | ::: 21 | 22 | + Request 23 | + Headers 24 | ``` 25 | X-Auth-User: a1133bc6-a0f6-46bf-a2d8-6157418c6fe2 26 | ``` 27 | 28 | 29 | + Response 200 30 | If your authorization token is valid. 31 | + Body 32 | 33 | 34 | 35 | + Response 403 36 | Authorization failed because your authorization token is invalid. 37 | + Body -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | require "active_model/railtie" 5 | require "active_job/railtie" 6 | require "active_record/railtie" 7 | require "action_controller/railtie" 8 | require "action_mailer/railtie" 9 | require "action_view/railtie" 10 | require "action_cable/engine" 11 | 12 | require "rails/test_unit/railtie" 13 | 14 | Bundler.require(*Rails.groups) 15 | 16 | module Judge0API 17 | class Application < Rails::Application 18 | config.api_only = true 19 | config.generators do |g| 20 | g.factory_girl test_framework: :rspec 21 | end 22 | 23 | config.active_job.queue_adapter = :resque 24 | 25 | config.middleware.insert_before 0, Rack::Cors do 26 | origins = [] 27 | 28 | disallowed_origins = (ENV['DISALLOW_ORIGIN'].to_s.split - ENV['ALLOW_ORIGIN'].to_s.split).collect{ |s| s.gsub(".", "\\.") }.join("|") 29 | if disallowed_origins.present? 30 | origins.append(Regexp.new("^(?:(?!#{disallowed_origins}).)*$")) 31 | end 32 | 33 | # ALLOW_ORIGIN and DISALLOW_ORIGIN are mutually exclusive so this doesn't have any effect. 34 | origins += (ENV['ALLOW_ORIGIN'].presence || (origins.present? ? '' : '*')).split 35 | 36 | allow do 37 | origins origins 38 | resource '*', headers: :any, methods: :any 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | nginx-proxy: 4 | image: jwilder/nginx-proxy 5 | container_name: judge0-dev-nginx-proxy 6 | volumes: 7 | - /var/run/docker.sock:/tmp/docker.sock:ro 8 | environment: 9 | - DEFAULT_HOST=api.judge0.local 10 | ports: 11 | - "8080:80" 12 | restart: always 13 | 14 | api: 15 | build: 16 | context: . 17 | dockerfile: Dockerfile.dev 18 | args: 19 | - DEV_USER=$JUDGE0_DEV_USER 20 | - DEV_USER_ID=$JUDGE0_DEV_UID 21 | image: judge0/api:dev 22 | container_name: judge0-dev-api 23 | environment: 24 | - VIRTUAL_HOST=api.judge0.local 25 | - VIRTUAL_PORT=3000 26 | volumes: 27 | - .:/api 28 | ports: 29 | - "3000:3000" 30 | - "3030:3030" 31 | privileged: true 32 | 33 | db: 34 | image: postgres:9.6 35 | container_name: judge0-dev-db 36 | env_file: judge0-api.conf 37 | volumes: 38 | - postgres-data:/var/lib/postgresql/data/ 39 | ports: 40 | - "5432:5432" 41 | restart: always 42 | 43 | redis: 44 | image: redis:4.0 45 | container_name: judge0-dev-redis 46 | command: [ 47 | "bash", "-c", 48 | 'docker-entrypoint.sh --appendonly yes --requirepass "$$REDIS_PASSWORD"' 49 | ] 50 | env_file: judge0-api.conf 51 | volumes: 52 | - redis-data:/data 53 | ports: 54 | - "6379:6379" 55 | restart: always 56 | 57 | volumes: 58 | postgres-data: 59 | redis-data: 60 | -------------------------------------------------------------------------------- /docs/api/authentication/authentication.md: -------------------------------------------------------------------------------- 1 | # Group Authentication 2 | Administrators of Judge0 API can configure Judge0 API to require you to have an authentication token (a.k.a. API key). 3 | If that is the case with the instance of Judge0 API you are using, then you 4 | should provide `X-Auth-Token` header field in **every** API request. 5 | 6 | ::: note 7 |

Note

8 | * For [official public API](#header-official-public-api) you **do not** need an authentication token. 9 | ::: 10 | 11 | ## Authenticate [/authenticate] 12 | ### Authenticate [POST] 13 | Check if your authentication token is valid. 14 | 15 | ::: note 16 |

Note

17 | * `X-Auth-Token` is default header field name, but administrators of Judge0 API instance you are using 18 | can change this default field name. 19 | * Contact administrator of Judge0 API instance you are using to get your authentication token. 20 | ::: 21 | 22 | ::: warning 23 |

Security Warning

24 | * Although you can send authentication token as URI parameter, **always** send authentication token through headers. 25 | ::: 26 | 27 | + Request 28 | + Headers 29 | ``` 30 | X-Auth-Token: f6583e60-b13b-4228-b554-2eb332ca64e7 31 | ``` 32 | 33 | + Response 200 34 | If your authentication token is valid or authentication is disabled. 35 | If authentication is disabled you do not need an authentication token. 36 | + Body 37 | 38 | + Response 401 39 | Authentication failed because your authentication token is invalid. 40 | + Body -------------------------------------------------------------------------------- /spec/support/active_model_serializer_matcher.rb: -------------------------------------------------------------------------------- 1 | RSpec::Matchers.define :have_serialized do |unserialized| 2 | chain :with do |serializer, options = {}| 3 | @serializer = serializer 4 | @options = options 5 | end 6 | 7 | chain :wrap do |collection_serializer, options = {}| 8 | @collection_serializer = collection_serializer 9 | end 10 | 11 | def serialize(object) 12 | @collection_serializer ||= ActiveModel::Serializer::CollectionSerializer 13 | 14 | if object.respond_to?(:map) 15 | serializer = @collection_serializer.new(object, serializer: @serializer) 16 | else 17 | serializer = @serializer.new(object) 18 | end 19 | 20 | json = ActiveModelSerializers::Adapter::Attributes.new(serializer, @options).to_json 21 | JSON.parse(json) 22 | end 23 | 24 | match do |serialized| 25 | serialized == serialize(unserialized) 26 | end 27 | 28 | failure_message do |serialized| 29 | <<-FAIL.strip_heredoc 30 | expected: 31 | #{serialized} 32 | to match: 33 | #{serialize(unserialized)} 34 | --- 35 | #{inspect} 36 | FAIL 37 | end 38 | 39 | failure_message_when_negated do |serialized| 40 | <<-FAIL.strip_heredoc 41 | expected 42 | #{serialized} 43 | not to match: 44 | #{serialize(unserialized)} 45 | --- 46 | #{inspect} 47 | FAIL 48 | end 49 | 50 | def inspect 51 | <<-INSPECT.strip_heredoc 52 | Using object serializer: #{@serializer} 53 | Using collection serializer: #{@collection_serializer} 54 | INSPECT 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /app/serializers/submission_serializer.rb: -------------------------------------------------------------------------------- 1 | class SubmissionSerializer < ActiveModel::Serializer 2 | attributes((Submission.column_names + ["status", "language"] - ["id"]).collect(&:to_sym)) 3 | 4 | def self.default_fields 5 | @@default_fields ||= [ 6 | :token, 7 | :time, 8 | :memory, 9 | :stdout, 10 | :stderr, 11 | :compile_output, 12 | :message, 13 | :status 14 | ] 15 | end 16 | 17 | def source_code 18 | object_decoder(:source_code) 19 | end 20 | 21 | def stdin 22 | object_decoder(:stdin) 23 | end 24 | 25 | def expected_output 26 | object_decoder(:expected_output) 27 | end 28 | 29 | def stdout 30 | object_decoder(:stdout) 31 | end 32 | 33 | def stderr 34 | object_decoder(:stderr) 35 | end 36 | 37 | def compile_output 38 | object_decoder(:compile_output) 39 | end 40 | 41 | def message 42 | if instance_options[:base64_encoded] and object.message 43 | return Base64Service.encode(object.message) 44 | end 45 | object.message 46 | end 47 | 48 | def status 49 | { id: object.status_id, description: object.status.name } 50 | end 51 | 52 | def language 53 | ActiveModelSerializers::SerializableResource.new(object.language, { serializer: LanguageSerializer, fields: [:id, :name] }) 54 | end 55 | 56 | def archive 57 | Base64Service.encode(object.archive) 58 | end 59 | 60 | private 61 | 62 | def object_decoder(method) 63 | instance_options[:base64_encoded] ? object[method] : object.send(method) 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /docs/api/statuses_and_languages/get_statuses.md: -------------------------------------------------------------------------------- 1 | ## Status [/statuses] 2 | ### Get Statuses [GET] 3 | + Response 200 (application/json) 4 | [ 5 | { 6 | "id": 1, 7 | "description": "In Queue" 8 | }, 9 | { 10 | "id": 2, 11 | "description": "Processing" 12 | }, 13 | { 14 | "id": 3, 15 | "description": "Accepted" 16 | }, 17 | { 18 | "id": 4, 19 | "description": "Wrong Answer" 20 | }, 21 | { 22 | "id": 5, 23 | "description": "Time Limit Exceeded" 24 | }, 25 | { 26 | "id": 6, 27 | "description": "Compilation Error" 28 | }, 29 | { 30 | "id": 7, 31 | "description": "Runtime Error (SIGSEGV)" 32 | }, 33 | { 34 | "id": 8, 35 | "description": "Runtime Error (SIGXFSZ)" 36 | }, 37 | { 38 | "id": 9, 39 | "description": "Runtime Error (SIGFPE)" 40 | }, 41 | { 42 | "id": 10, 43 | "description": "Runtime Error (SIGABRT)" 44 | }, 45 | { 46 | "id": 11, 47 | "description": "Runtime Error (NZEC)" 48 | }, 49 | { 50 | "id": 12, 51 | "description": "Runtime Error (Other)" 52 | }, 53 | { 54 | "id": 13, 55 | "description": "Internal Error" 56 | }, 57 | { 58 | "id": 14, 59 | "description": "Exec Format Error" 60 | } 61 | ] 62 | -------------------------------------------------------------------------------- /RELEASE_NOTES_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # vX.Y.Z (YYYY-MM-DD) 2 | ## API Changes 3 | 4 | ## New Features 5 | 6 | ## Improvements 7 | 8 | ## Security Improvements 9 | 10 | ## Bug Fixes 11 | 12 | ## Security Fixes 13 | 14 | ## Other Changes 15 | 16 | ## Deployment Procedure 17 | ### With HTTPS (SSL/TLS) 18 | 1. Install [Docker](https://docs.docker.com) and [Docker Compose](https://docs.docker.com/compose). 19 | 2. Download and extract release archive: 20 | ``` 21 | wget https://github.com/judge0/api/releases/download/vX.Y.Z/judge0-api-vX.Y.Z-https.zip 22 | unzip judge0-api-vX.Y.Z-https.zip 23 | ``` 24 | 25 | 3. Change directory to `judge0-api-vX.Y.Z-https`: 26 | ``` 27 | cd judge0-api-vX.Y.Z-https 28 | ``` 29 | 4. Edit `docker-compose.yml` and change variables `VIRTUAL_HOST`, `LETSENCRYPT_HOST` and `LETSENCRYPT_EMAIL`. 30 | 5. Run all services and wait few seconds until everything is initialized: 31 | ``` 32 | docker-compose up -d db redis 33 | sleep 10s 34 | docker-compose up -d 35 | sleep 5s 36 | ``` 37 | 38 | 6. Your instance of Judge0 API vX.Y.Z is now available at `https://`. 39 | 40 | ### With HTTP 41 | 1. Install [Docker](https://docs.docker.com) and [Docker Compose](https://docs.docker.com/compose). 42 | 2. Download and extract release archive: 43 | ``` 44 | wget https://github.com/judge0/api/releases/download/vX.Y.Z/judge0-api-vX.Y.Z.zip 45 | unzip judge0-api-vX.Y.Z.zip 46 | ``` 47 | 48 | 3. Run all services and wait few seconds until everything is initialized: 49 | ``` 50 | cd judge0-api-vX.Y.Z 51 | docker-compose up -d db redis 52 | sleep 10s 53 | docker-compose up -d 54 | sleep 5s 55 | ``` 56 | 57 | 4. Your instance of Judge0 API vX.Y.Z is now available at `http://`. -------------------------------------------------------------------------------- /docs/api/submissions/delete_a_submission.md: -------------------------------------------------------------------------------- 1 | ### Delete a Submission [DELETE] 2 | Delete specific submission. 3 | 4 | You need to be authorized to issue this request. Although you are 5 | authorized you might not be able to delete submission because administrator of Judge0 API instance 6 | you are using disallowed submission deletion. So before using this feature please check [configuration](#system-and-configuration-configuration-info-get) of Judge0 API you are using. 7 | 8 | For this request query parameter `base64_encoded` is implicitly set to `true` and cannot be changed. 9 | This guarantees you will successfully get requested submission attributes after deletion. 10 | 11 | + Parameters 12 | + token (required, string, `d85cd024-1548-4165-96c7-7bc88673f194`) ... Token of submission. You got this token when you created submission. 13 | + fields = `stdout,time,memory,stderr,token,compile_output,message,status` (optional, string, `stdout,stderr,status_id,language_id`) ... Return only the desired attributes. 14 | 15 | + Response 200 (applcation/json) 16 | + Body 17 | { 18 | "stdout": "aGVsbG8sIHdvcmxkCg==\n", 19 | "time": "0.045", 20 | "memory": 8556, 21 | "stderr": null, 22 | "token": "e80153f5-e7d8-4cd2-9e10-6c0ddbf9e3bf", 23 | "compile_output": null, 24 | "message": null, 25 | "status": { 26 | "id": 3, 27 | "description": "Accepted" 28 | } 29 | } 30 | 31 | + Response 400 (application/json) 32 | If submission status is `1` or `2`. 33 | + Body 34 | { 35 | "error": "submission cannot be deleted because its status is 1 (In Queue)" 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | 41 | # Raises error for missing translations 42 | # config.action_view.raise_on_missing_translations = true 43 | 44 | # Use an evented file watcher to asynchronously detect changes in source code, 45 | # routes, locales, etc. This feature depends on the listen gem. 46 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 47 | end 48 | -------------------------------------------------------------------------------- /docs/api/system_and_configuration/system_info.md: -------------------------------------------------------------------------------- 1 | ## System Info [/system_info] 2 | ### System Info [GET] 3 | System information gives you detailed information about system on which Judge0 API is running. 4 | 5 | This information is result of two commands on a host system: 6 | - `lscpu` 7 | - `free -h` 8 | 9 | Please note that Judge0 API consists of two systems: **web** and **worker**. **Web** system is the one who 10 | provides you the web API, and **Worker** is the one who processes your submissions. They can be placed on two or more 11 | different hosts with different system configurations. Result of this API request is always from web system. 12 | This means that this request might be irrelevant to you if you as user don't know if web and worker are 13 | hosted on the same machine. To find that out, please contact admins who host Judge0 API you are using. 14 | + Response 200 (application/json) 15 | { 16 | "Architecture": "x86_64", 17 | "CPU op-mode(s)": "32-bit, 64-bit", 18 | "Byte Order": "Little Endian", 19 | "CPU(s)": "4", 20 | "On-line CPU(s) list": "0-3", 21 | "Thread(s) per core": "2", 22 | "Core(s) per socket": "2", 23 | "Socket(s)": "1", 24 | "NUMA node(s)": "1", 25 | "Vendor ID": "GenuineIntel", 26 | "CPU family": "6", 27 | "Model": "61", 28 | "Model name": "Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz", 29 | "Stepping": "4", 30 | "CPU MHz": "2508.703", 31 | "CPU max MHz": "2700.0000", 32 | "CPU min MHz": "500.0000", 33 | "BogoMIPS": "4392.12", 34 | "Virtualization": "VT-x", 35 | "L1d cache": "32K", 36 | "L1i cache": "32K", 37 | "L2 cache": "256K", 38 | "L3 cache": "3072K", 39 | "NUMA node0 CPU(s)": "0-3", 40 | "Mem": "7.7G", 41 | "Swap": "8.0G" 42 | } -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ActionController::API 2 | before_action :verify_ip_address 3 | before_action :authenticate_request 4 | before_action :authorize_request, only: [:authorize] 5 | 6 | def authenticate 7 | head :ok 8 | end 9 | 10 | def authorize 11 | head :ok 12 | end 13 | 14 | private 15 | 16 | def verify_ip_address 17 | @@disallowed_ip_addresses ||= ENV['DISALLOW_IP'].to_s.split - ENV['ALLOW_IP'].to_s.split 18 | @@allowed_ip_addresses ||= ENV['ALLOW_IP'].to_s.split - ENV['DISALLOW_IP'].to_s.split 19 | head :forbidden if @@disallowed_ip_addresses.include?(request.remote_ip) 20 | head :forbidden if @@allowed_ip_addresses.present? && !@@allowed_ip_addresses.include?(request.remote_ip) 21 | end 22 | 23 | def authenticate_request 24 | head :unauthorized if safe_compare(Rails.application.secrets.authn_token, Rails.application.secrets.authn_header) 25 | end 26 | 27 | def authorize_request 28 | head :forbidden unless Rails.application.secrets.authz_token.present? 29 | head :forbidden if safe_compare(Rails.application.secrets.authz_token, Rails.application.secrets.authz_header) 30 | end 31 | 32 | def check_maintenance 33 | @@maintenance_message ||= ENV['MAINTENANCE_MESSAGE'].to_s.presence || "Judge0 API is currently in maintenance." 34 | if Config::MAINTENANCE_MODE 35 | render json: { 36 | error: @@maintenance_message 37 | }, status: :service_unavailable 38 | end 39 | end 40 | 41 | def safe_compare(token, header) 42 | token = token.to_s 43 | header = header.to_s 44 | return false unless token.present? 45 | provided_token = (request.headers[header] || params[header]).to_s 46 | token.split.each do |value| 47 | return false if ActiveSupport::SecurityUtils.secure_compare(value, provided_token) 48 | end 49 | true 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /lib/tasks/auto_annotate_models.rake: -------------------------------------------------------------------------------- 1 | if Rails.env.development? 2 | task :set_annotation_options do 3 | # You can override any of these by setting an environment variable of the 4 | # same name. 5 | Annotate.set_defaults( 6 | 'routes' => 'false', 7 | 'position_in_routes' => 'before', 8 | 'position_in_class' => 'before', 9 | 'position_in_test' => 'before', 10 | 'position_in_fixture' => 'before', 11 | 'position_in_factory' => 'before', 12 | 'position_in_serializer' => 'before', 13 | 'show_foreign_keys' => 'true', 14 | 'show_indexes' => 'true', 15 | 'simple_indexes' => 'false', 16 | 'model_dir' => 'app/models', 17 | 'root_dir' => '', 18 | 'include_version' => 'false', 19 | 'require' => '', 20 | 'exclude_tests' => 'true', 21 | 'exclude_fixtures' => 'true', 22 | 'exclude_factories' => 'true', 23 | 'exclude_serializers' => 'true', 24 | 'exclude_scaffolds' => 'true', 25 | 'exclude_controllers' => 'true', 26 | 'exclude_helpers' => 'true', 27 | 'ignore_model_sub_dir' => 'false', 28 | 'ignore_columns' => nil, 29 | 'ignore_routes' => nil, 30 | 'ignore_unknown_models' => 'false', 31 | 'hide_limit_column_types' => 'integer,boolean', 32 | 'skip_on_db_migrate' => 'false', 33 | 'format_bare' => 'true', 34 | 'format_rdoc' => 'false', 35 | 'format_markdown' => 'false', 36 | 'sort' => 'false', 37 | 'force' => 'false', 38 | 'trace' => 'false', 39 | 'wrapper_open' => nil, 40 | 'wrapper_close' => nil 41 | ) 42 | end 43 | 44 | Annotate.load_tasks 45 | end 46 | -------------------------------------------------------------------------------- /spec/controllers/submissions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe SubmissionsController, type: :controller do 4 | 5 | let(:submission) { create(:submission) } 6 | 7 | describe "GET #show" do 8 | it "returns one submission" do 9 | get :show, params: { token: submission.token } 10 | json = JSON.parse(response.body) 11 | expect(response).to be_success 12 | expect(json).to have_serialized(submission).with(SubmissionSerializer) 13 | end 14 | 15 | it "returns 404" do 16 | expect { 17 | get :show, params: { token: submission.token + " give me 404" } 18 | }.to raise_error(ActiveRecord::RecordNotFound) 19 | end 20 | end 21 | 22 | describe "POST #create" do 23 | context "with valid params" do 24 | it "creates a new Submission" do 25 | expect { 26 | post :create, params: attributes_for(:valid_submission) 27 | }.to change(Submission, :count).by(1) 28 | end 29 | 30 | it "returns only id of new Submission" do 31 | post :create, params: attributes_for(:valid_submission) 32 | json = JSON.parse(response.body) 33 | expect(response).to be_success 34 | expect(json).to have_serialized(Submission.first).with(SubmissionSerializer, { fields: [:token] }) 35 | end 36 | 37 | it "doesn't create new Submission because given Language doesn't exist" do 38 | attributes = attributes_for(:valid_submission) 39 | attributes[:language_id] = 142 # Language with id 142 doesn't exist 40 | post :create, params: attributes 41 | expect(response).to have_http_status(422) 42 | end 43 | end 44 | 45 | context "with invalid params" do 46 | it "doesn't create new Submission" do 47 | post :create, params: { submission: attributes_for(:invalid_submission) } 48 | expect(response).to have_http_status(422) 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20200115205044) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "plpgsql" 17 | 18 | create_table "languages", force: :cascade do |t| 19 | t.string "name" 20 | t.string "compile_cmd" 21 | t.string "run_cmd" 22 | t.string "source_file" 23 | t.boolean "is_archived", default: false 24 | end 25 | 26 | create_table "submissions", force: :cascade do |t| 27 | t.text "source_code" 28 | t.integer "language_id" 29 | t.text "stdin" 30 | t.text "expected_output" 31 | t.text "stdout" 32 | t.integer "status_id" 33 | t.datetime "created_at" 34 | t.datetime "finished_at" 35 | t.decimal "time" 36 | t.integer "memory" 37 | t.text "stderr" 38 | t.string "token" 39 | t.integer "number_of_runs" 40 | t.decimal "cpu_time_limit" 41 | t.decimal "cpu_extra_time" 42 | t.decimal "wall_time_limit" 43 | t.integer "memory_limit" 44 | t.integer "stack_limit" 45 | t.integer "max_processes_and_or_threads" 46 | t.boolean "enable_per_process_and_thread_time_limit" 47 | t.boolean "enable_per_process_and_thread_memory_limit" 48 | t.integer "max_file_size" 49 | t.text "compile_output" 50 | t.integer "exit_code" 51 | t.integer "exit_signal" 52 | t.text "message" 53 | t.decimal "wall_time" 54 | t.string "compiler_options" 55 | t.string "command_line_arguments" 56 | t.boolean "redirect_stderr_to_stdout" 57 | t.string "callback_url" 58 | t.binary "archive" 59 | t.index ["token"], name: "index_submissions_on_token", using: :btree 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /app/controllers/info_controller.rb: -------------------------------------------------------------------------------- 1 | class InfoController < ApplicationController 2 | @@license ||= File.read("LICENSE") 3 | @@isolate ||= `isolate --version` 4 | 5 | def system_info 6 | render json: SystemInfo.sys_info 7 | end 8 | 9 | def config_info 10 | render json: Config.config_info 11 | end 12 | 13 | def about 14 | render json: { 15 | version: ENV["JUDGE0_VERSION"], 16 | homepage: ENV["JUDGE0_HOMEPAGE"], 17 | source_code: ENV["JUDGE0_SOURCE_CODE"], 18 | maintainer: ENV["JUDGE0_MAINTAINER"] 19 | } 20 | end 21 | 22 | def version 23 | render plain: ENV["JUDGE0_VERSION"] 24 | end 25 | 26 | def license 27 | render plain: @@license 28 | end 29 | 30 | def isolate 31 | render plain: @@isolate 32 | end 33 | 34 | def statistics 35 | @@language_name ||= Hash[Language.unscoped.pluck(:id, :name)] 36 | 37 | count_by_language = [] 38 | Submission.unscoped.group(:language_id).count.each do |language_id, count| 39 | count_by_language << { 40 | language: { 41 | id: language_id, 42 | name: @@language_name[language_id] 43 | }, 44 | count: count 45 | } 46 | end 47 | count_by_language = count_by_language.sort_by { |x| x[:count] }.reverse 48 | 49 | count_by_status = [] 50 | Submission.unscoped.group(:status_id).count.each do |status_id, count| 51 | count_by_status << { 52 | status: { 53 | id: status_id, 54 | name: Status.find_by(id: status_id).name # Not a SQL query! 55 | }, 56 | count: count 57 | } 58 | end 59 | count_by_status = count_by_status.sort_by{ |x| x[:count] }.reverse 60 | 61 | now = DateTime.now 62 | today = DateTime.now.beginning_of_day.to_date 63 | last_30_days = Submission.unscoped.group("created_at::DATE").where("created_at::DATE >= ?", today - 30).count 64 | last_30_days_result = {} 65 | (today-30...today).each do |day| 66 | last_30_days_result[day.to_date] = last_30_days[day] || 0 67 | end 68 | last_30_days_result = last_30_days_result.sort.reverse.to_h 69 | 70 | database_size = ActiveRecord::Base.connection.execute("SELECT pg_size_pretty(pg_database_size('#{ENV['POSTGRES_DB']}')) AS size").to_a[0]["size"] 71 | 72 | render json: { 73 | now: now, 74 | submissions: { 75 | total: Submission.count, 76 | today: last_30_days[today], 77 | last_30_days: last_30_days_result 78 | }, 79 | languages: count_by_language, 80 | statuses: count_by_status, 81 | database: { 82 | size: database_size 83 | } 84 | } 85 | end 86 | end -------------------------------------------------------------------------------- /docs/api/docs.md: -------------------------------------------------------------------------------- 1 | FORMAT: 1A 2 | HOST: https://api.judge0.com 3 | 4 | # Judge0 API docs 5 | 6 | 7 | 8 | ## About 9 | [Judge0](https://judge0.com) is a non-profit organization that creates free and open-source tools and services for executing and grading untrusted source code. 10 | 11 | [Judge0 API](https://api.judge0.com) is a robust and scalable open-source (source code available on [GitHub](https://github.com/judge0/api)) online code execution system made by Judge0. 12 | 13 | Judge0 API can help you build wide range of applications varying from competitive programming platforms, 14 | educational and recruitment platforms, to online code editors and more. 15 | 16 | To see Judge0 API in action, try [Judge0 IDE](https://ide.judge0.com) - free and open-source code editor that uses Judge0 API for executing the user’s source code. You can also try using a [dummy client](/dummy-client.html), which can help you explore and test almost all the features of Judge0 API. 17 | 18 | ## Official public API 19 | Official public API is available at [https://api.judge0.com](https://api.judge0.com) and is maintained by Judge0 [creator](https://github.com/hermanzdosilovic). 20 | 21 | Status page of all public Judge0 services is available at [https://status.judge0.com](https://status.judge0.com). 22 | 23 | ::: note 24 |

Note

25 | * You **do not** need an authentication token (a.k.a. API key) for official public API. 26 | * Official public API will always be at the latest available version. 27 | ::: 28 | 29 | ## Version 30 | This document describes Judge0 API version [`v1.5.0`](https://github.com/judge0/api/tree/v1.5.0). 31 | 32 | ## Date and time formats 33 | [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard is used. 34 | 35 | Example: `2016-09-11T10:19:35Z` 36 | 37 | ## License 38 | Judge0 API is licensed under the [GNU General Public License v3.0](https://github.com/judge0/api/blob/master/LICENSE). 39 | 40 | ## Donate 41 | Your are more than welcome to support Judge0 on [Patreon](https://www.patreon.com/hermanzdosilovic) or via [PayPal](https://www.paypal.me/hermanzdosilovic). Your monthly or one-time donation will help pay server costs and will improve future development and maintenance. Thank you ♥ 42 | 43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /docs/api/submissions/get_a_submission.md: -------------------------------------------------------------------------------- 1 | ### Get a Submission [GET] 2 | Returns details about submission. 3 | 4 | Just like in [create submission](/#submission-submission-post) you can receive Base64 encoded data for every 5 | text type attribute (check the [table](#submission-submission) to see which attributes are text type). 6 | By default, this parameter is set to `false` and Judge0 API will send you raw data. 7 | 8 | By default Judge0 API is sending you 8 attributes for submission. As you may read in [Submission](#submission-submission) 9 | section, submission model has 29 attributes. By sending `fields` query parameter you can specify exactly which attributes 10 | you want from Judge0 API. 11 | 12 | + Parameters 13 | + token (required, string, `d85cd024-1548-4165-96c7-7bc88673f194`) ... Token of submission. You got this token when you created submission. 14 | + base64_encoded (optional, boolean, `false`) ... Set to `true` if you want to receive Base64 encoded data from Judge0 API. 15 | + fields = `stdout,time,memory,stderr,token,compile_output,message,status` (optional, string, `stdout,stderr,status_id,language_id`) ... Return only the desired attributes. 16 | 17 | + Response 200 (applicatiion/json) 18 | { 19 | "stdout": "hello, world\n", 20 | "status_id": 5, 21 | "language_id": 4, 22 | "stderr": null 23 | } 24 | 25 | + Response 200 (application/json) 26 | This is the default response. Leave `fields` parameter empty if you want to get default response. 27 | + Body 28 | { 29 | "stdout": "hello, Judge0\n", 30 | "time": "0.001", 31 | "memory": 376, 32 | "stderr": null, 33 | "token": "8531f293-1585-4d36-a34c-73726792e6c9", 34 | "compile_output": null, 35 | "message": null, 36 | "status": { 37 | "id": 3, 38 | "description": "Accepted" 39 | } 40 | } 41 | 42 | + Response 200 (application/json) 43 | Recieving Base64 encoded data for text type attributes. Note that in this request `base64_encoded` query parameter **must** be 44 | set to `true`. 45 | + Body 46 | { 47 | "stdout": "aGVsbG8sIEp1ZGdlMAo=\n", 48 | "time": "0.002", 49 | "memory": 376, 50 | "stderr": null, 51 | "token": "4e00f214-b8cb-4fcb-977b-429113c81ece", 52 | "compile_output": null, 53 | "message": null, 54 | "status": { 55 | "id": 3, 56 | "description": "Accepted" 57 | } 58 | } 59 | 60 | + Response 400 (application/json) 61 | + Body 62 | { 63 | "error": "some attributes for this submission cannot be converted to UTF-8, use base64_encoded=true query parameter" 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /docs/api/statuses_and_languages/get_languages.md: -------------------------------------------------------------------------------- 1 | ### Get Languages [GET] 2 | Get active languages. 3 | 4 | + Response 200 (application/json) 5 | [ 6 | { 7 | "id": 45, 8 | "name": "Assembly (NASM 2.14.02)" 9 | }, 10 | { 11 | "id": 46, 12 | "name": "Bash (5.0.0)" 13 | }, 14 | { 15 | "id": 47, 16 | "name": "Basic (FBC 1.07.1)" 17 | }, 18 | { 19 | "id": 48, 20 | "name": "C (GCC 7.4.0)" 21 | }, 22 | { 23 | "id": 52, 24 | "name": "C++ (GCC 7.4.0)" 25 | }, 26 | { 27 | "id": 49, 28 | "name": "C (GCC 8.3.0)" 29 | }, 30 | { 31 | "id": 53, 32 | "name": "C++ (GCC 8.3.0)" 33 | }, 34 | { 35 | "id": 50, 36 | "name": "C (GCC 9.2.0)" 37 | }, 38 | { 39 | "id": 54, 40 | "name": "C++ (GCC 9.2.0)" 41 | }, 42 | { 43 | "id": 51, 44 | "name": "C# (Mono 6.6.0.161)" 45 | }, 46 | { 47 | "id": 55, 48 | "name": "Common Lisp (SBCL 2.0.0)" 49 | }, 50 | { 51 | "id": 56, 52 | "name": "D (DMD 2.089.1)" 53 | }, 54 | { 55 | "id": 57, 56 | "name": "Elixir (1.9.4)" 57 | }, 58 | { 59 | "id": 58, 60 | "name": "Erlang (OTP 22.2)" 61 | }, 62 | { 63 | "id": 44, 64 | "name": "Executable" 65 | }, 66 | { 67 | "id": 59, 68 | "name": "Fortran (GFortran 9.2.0)" 69 | }, 70 | { 71 | "id": 60, 72 | "name": "Go (1.13.5)" 73 | }, 74 | { 75 | "id": 61, 76 | "name": "Haskell (GHC 8.8.1)" 77 | }, 78 | { 79 | "id": 62, 80 | "name": "Java (OpenJDK 13.0.1)" 81 | }, 82 | { 83 | "id": 63, 84 | "name": "JavaScript (Node.js 12.14.0)" 85 | }, 86 | { 87 | "id": 64, 88 | "name": "Lua (5.3.5)" 89 | }, 90 | { 91 | "id": 65, 92 | "name": "OCaml (4.09.0)" 93 | }, 94 | { 95 | "id": 66, 96 | "name": "Octave (5.1.0)" 97 | }, 98 | { 99 | "id": 67, 100 | "name": "Pascal (FPC 3.0.4)" 101 | }, 102 | { 103 | "id": 68, 104 | "name": "PHP (7.4.1)" 105 | }, 106 | { 107 | "id": 43, 108 | "name": "Plain Text" 109 | }, 110 | { 111 | "id": 69, 112 | "name": "Prolog (GNU Prolog 1.4.5)" 113 | }, 114 | { 115 | "id": 70, 116 | "name": "Python (2.7.17)" 117 | }, 118 | { 119 | "id": 71, 120 | "name": "Python (3.8.1)" 121 | }, 122 | { 123 | "id": 72, 124 | "name": "Ruby (2.7.0)" 125 | }, 126 | { 127 | "id": 73, 128 | "name": "Rust (1.40.0)" 129 | }, 130 | { 131 | "id": 74, 132 | "name": "TypeScript (3.7.4)" 133 | } 134 | ] -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Disable serving static files from the `/public` folder by default since 18 | # Apache or NGINX already handles this. 19 | config.public_file_server.enabled = true 20 | 21 | 22 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 23 | # config.action_controller.asset_host = 'http://assets.example.com' 24 | 25 | # Specifies the header that your server uses for sending files. 26 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 27 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 28 | 29 | # Mount Action Cable outside main process or domain 30 | # config.action_cable.mount_path = nil 31 | # config.action_cable.url = 'wss://example.com/cable' 32 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 33 | 34 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 35 | # config.force_ssl = true 36 | 37 | # Use the lowest log level to ensure availability of diagnostic information 38 | # when problems arise. 39 | config.log_level = :debug 40 | 41 | # Prepend all log lines with the following tags. 42 | config.log_tags = [ :request_id ] 43 | 44 | # Use a different cache store in production. 45 | # config.cache_store = :mem_cache_store 46 | 47 | # Use a real queuing backend for Active Job (and separate queues per environment) 48 | # config.active_job.queue_adapter = :resque 49 | # config.active_job.queue_name_prefix = "Judge0API_#{Rails.env}" 50 | config.action_mailer.perform_caching = false 51 | 52 | # Ignore bad email addresses and do not raise email delivery errors. 53 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 54 | # config.action_mailer.raise_delivery_errors = false 55 | 56 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 57 | # the I18n.default_locale when a translation cannot be found). 58 | config.i18n.fallbacks = [I18n.default_locale] 59 | 60 | # Send deprecation notices to registered listeners. 61 | config.active_support.deprecation = :notify 62 | 63 | # Use default logging formatter so that PID and timestamp are not suppressed. 64 | config.log_formatter = ::Logger::Formatter.new 65 | 66 | # Use a different logger for distributed setups. 67 | # require 'syslog/logger' 68 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 69 | 70 | if ENV["RAILS_LOG_TO_STDOUT"].present? 71 | logger = ActiveSupport::Logger.new(STDOUT) 72 | logger.formatter = config.log_formatter 73 | config.logger = ActiveSupport::TaggedLogging.new(logger) 74 | end 75 | 76 | # Do not dump schema after migrations. 77 | config.active_record.dump_schema_after_migration = false 78 | end 79 | -------------------------------------------------------------------------------- /docs/api/submissions/get_submissions.md: -------------------------------------------------------------------------------- 1 | ### Get Submissions [GET] 2 | + Parameters 3 | + base64_encoded = `false` (optional, boolean, `false`) ... Set to `true` if you want to receive Base64 encoded data from Judge0 API. 4 | + page = `1` (optional, integer, `4`) ... Pagination page number. 5 | + per_page = `20` (optional, integer, `2`) ... Number of submissions to return per page. 6 | + fields = `stdout,time,memory,stderr,token,compile_output,message,status` (optional, string, `status,language,time`) ... Return only the desired attributes. 7 | 8 | + Response 200 (application/json) 9 | { 10 | "submissions": [ 11 | { 12 | "time": "0.001", 13 | "status": { 14 | "id": 3, 15 | "description": "Accepted" 16 | }, 17 | "language": { 18 | "id": 4, 19 | "name": "C (gcc 7.2.0)" 20 | } 21 | }, 22 | { 23 | "time": "0.001", 24 | "status": { 25 | "id": 3, 26 | "description": "Accepted" 27 | }, 28 | "language": { 29 | "id": 4, 30 | "name": "C (gcc 7.2.0)" 31 | } 32 | } 33 | ], 34 | "meta": { 35 | "current_page": 4, 36 | "next_page": 5, 37 | "prev_page": 3, 38 | "total_pages": 31, 39 | "total_count": 62 40 | } 41 | } 42 | 43 | + Response 200 (application/json) 44 | When `base64_encoded` is set to `true`. 45 | + Body 46 | { 47 | "submissions": [ 48 | { 49 | "stdout": "aGVsbG8sIEp1ZGdlMAo=\n", 50 | "time": "0.001", 51 | "memory": 376, 52 | "stderr": null, 53 | "token": "a1133bc6-a0f6-46bf-a2d8-6157418c6fe2", 54 | "compile_output": null, 55 | "message": null, 56 | "status": { 57 | "id": 3, 58 | "description": "Accepted" 59 | } 60 | }, 61 | { 62 | "stdout": "aGVsbG8sIEp1ZGdlMAo=\n", 63 | "time": "0.001", 64 | "memory": 380, 65 | "stderr": null, 66 | "token": "eb0dd001-66db-47f4-8a69-b736c9bc23f6", 67 | "compile_output": null, 68 | "message": null, 69 | "status": { 70 | "id": 3, 71 | "description": "Accepted" 72 | } 73 | } 74 | ], 75 | "meta": { 76 | "current_page": 4, 77 | "next_page": 5, 78 | "prev_page": 3, 79 | "total_pages": 31, 80 | "total_count": 62 81 | } 82 | } 83 | 84 | + Response 400 (application/json) 85 | When `page` parameter is invalid. 86 | + Body 87 | { 88 | "error": "invalid page: -4" 89 | } 90 | 91 | + Response 400 (application/json) 92 | When `per_page` parameter is invalid. 93 | + Body 94 | { 95 | "error": "invalid per_page: -2" 96 | } 97 | 98 | + Response 400 (application/json) 99 | + Body 100 | { 101 | "error": "some attributes for one or more submissions cannot be converted to UTF-8, use base64_encoded=true query parameter" 102 | } 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /app/helpers/config.rb: -------------------------------------------------------------------------------- 1 | module Config 2 | # For more info read: 3 | # https://github.com/judge0/api/blob/master/judge0-api.conf.default 4 | 5 | MAINTENANCE_MODE = ENV['MAINTENANCE_MODE'] == "true" 6 | ENABLE_WAIT_RESULT = ENV['ENABLE_WAIT_RESULT'] != "false" 7 | ENABLE_COMPILER_OPTIONS = ENV["ENABLE_COMPILER_OPTIONS"] != "false" 8 | ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS = ENV["ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS"].to_s.strip.split 9 | ENABLE_COMMAND_LINE_ARGUMENTS = ENV["ENABLE_COMMAND_LINE_ARGUMENTS"] != "false" 10 | ENABLE_SUBMISSION_DELETE = ENV["ENABLE_SUBMISSION_DELETE"] == "true" 11 | MAX_QUEUE_SIZE = (ENV['MAX_QUEUE_SIZE'].presence || 100).to_i 12 | CPU_TIME_LIMIT = (ENV['CPU_TIME_LIMIT'].presence || 2).to_f 13 | MAX_CPU_TIME_LIMIT = (ENV['MAX_CPU_TIME_LIMIT'].presence || 15).to_f 14 | CPU_EXTRA_TIME = (ENV['CPU_EXTRA_TIME'].presence || 0.5).to_f 15 | MAX_CPU_EXTRA_TIME = (ENV['MAX_CPU_EXTRA_TIME'].presence || 2).to_f 16 | WALL_TIME_LIMIT = (ENV['WALL_TIME_LIMIT'].presence || 5).to_f 17 | MAX_WALL_TIME_LIMIT = (ENV['MAX_WALL_TIME_LIMIT'].presence || 20).to_f 18 | MEMORY_LIMIT = (ENV['MEMORY_LIMIT'].presence || 128000).to_i # in KB 19 | MAX_MEMORY_LIMIT = (ENV['MAX_MEMORY_LIMIT'].presence || 256000).to_i 20 | STACK_LIMIT = (ENV['STACK_LIMIT'].presence || 64000).to_i # in KB 21 | MAX_STACK_LIMIT = (ENV['MAX_STACK_LIMIT'].presence || 128000).to_i 22 | MAX_PROCESSES_AND_OR_THREADS = (ENV['MAX_PROCESSES_AND_OR_THREADS'].presence || 60).to_i 23 | MAX_MAX_PROCESSES_AND_OR_THREADS = (ENV['MAX_MAX_PROCESSES_AND_OR_THREADS'].presence || 120).to_i 24 | ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT = ENV['ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT'] == "true" 25 | ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT = ENV['ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT'] != "false" 26 | ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT = ENV['ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT'] == "true" 27 | ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT = ENV['ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT'] != "false" 28 | MAX_FILE_SIZE = (ENV['MAX_FILE_SIZE'].presence || 1024).to_i 29 | MAX_MAX_FILE_SIZE = (ENV['MAX_MAX_FILE_SIZE'].presence || 4096).to_i 30 | NUMBER_OF_RUNS = (ENV['NUMBER_OF_RUNS'].presence || 1).to_i 31 | MAX_NUMBER_OF_RUNS = (ENV['MAX_NUMBER_OF_RUNS'].presence || 20).to_i 32 | REDIRECT_STDERR_TO_STDOUT = ENV['REDIRECT_STDERR_TO_STDOUT'] == "true" 33 | MAX_EXTRACT_SIZE = (ENV['MAX_EXTRACT_SIZE'].presence || 10240).to_i 34 | MAX_SUBMISSION_BATCH_SIZE = (ENV['MAX_SUBMISSION_BATCH_SIZE'].presence || 20).to_i 35 | 36 | def self.config_info 37 | @@default_confg ||= { 38 | "maintenance_mode": MAINTENANCE_MODE, 39 | "enable_wait_result": ENABLE_WAIT_RESULT, 40 | "enable_compiler_options": ENABLE_COMPILER_OPTIONS, 41 | "allowed_languages_for_compile_options": ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS, 42 | "enable_command_line_arguments": ENABLE_COMMAND_LINE_ARGUMENTS, 43 | "enable_submission_delete": ENABLE_SUBMISSION_DELETE, 44 | "max_queue_size": MAX_QUEUE_SIZE, 45 | "cpu_time_limit": CPU_TIME_LIMIT, 46 | "max_cpu_time_limit": MAX_CPU_TIME_LIMIT, 47 | "cpu_extra_time": CPU_EXTRA_TIME, 48 | "max_cpu_extra_time": MAX_CPU_EXTRA_TIME, 49 | "wall_time_limit": WALL_TIME_LIMIT, 50 | "max_wall_time_limit": MAX_WALL_TIME_LIMIT, 51 | "memory_limit": MEMORY_LIMIT, 52 | "max_memory_limit": MAX_MEMORY_LIMIT, 53 | "stack_limit": STACK_LIMIT, 54 | "max_stack_limit": MAX_STACK_LIMIT, 55 | "max_processes_and_or_threads": MAX_PROCESSES_AND_OR_THREADS, 56 | "max_max_processes_and_or_threads": MAX_MAX_PROCESSES_AND_OR_THREADS, 57 | "enable_per_process_and_thread_time_limit": ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT, 58 | "allow_enable_per_process_and_thread_time_limit": ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT, 59 | "enable_per_process_and_thread_memory_limit": ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT, 60 | "allow_enable_per_process_and_thread_memory_limit": ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT, 61 | "max_file_size": MAX_FILE_SIZE, 62 | "max_max_file_size": MAX_MAX_FILE_SIZE, 63 | "number_of_runs": NUMBER_OF_RUNS, 64 | "max_number_of_runs": MAX_NUMBER_OF_RUNS, 65 | "redirect_stderr_to_stdout": REDIRECT_STDERR_TO_STDOUT, 66 | "max_extract_size": MAX_EXTRACT_SIZE, 67 | "max_submission_batch_size": MAX_SUBMISSION_BATCH_SIZE 68 | } 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /docs/api/submissions/submissions.md: -------------------------------------------------------------------------------- 1 | ## Group Submissions 2 | ## Submission [/submissions/{token}{?base64_encoded,wait,fields,page,per_page}] 3 | Submission represents a model for running source code in specific programming language under 4 | certain runtime constraints. 5 | 6 | Submission model has **29** attributes. Attributes **1**-**16** (listed below) are used when creating new submissions - you can set them. Out of those 16 attributes only **2** are required - `source_code` and `language_id`. Attributes **17**-**29** represent detailed information about runtime of submission after it's execution. 7 | 8 | With attributes **7**-**16** you can configure submission runtime constraints. Those attributes are called *configuration attributes* or *configuration variables*. Please read more about them in [configuration](#system-and-configuration-configuration-info) section. 9 | 10 | Submission model has following attributes: 11 | |# |Name |Type |Unit |Description |Default Value | 12 | |:---:|:----|:----:|:----:|:-----------|:-------------| 13 | |1|**`source_code`**|text||Program's source code.|No default. This attribute is **required**.| 14 | |2|**`language_id`**|integer||[Language](#statuses-and-languages-language) ID.|No default. This attribute is **required**| 15 | |3|`compiler_options`|string (max. 128 chars)||Options for the compiler (i.e. compiler flags).|`null`| 16 | |4|`command_line_arguments`|string (max. 128 chars)||Command line arguments for the program.|`null`| 17 | |5|`stdin`|text||Input for program.|`null`. Program won't receive anything to standard input.| 18 | |6|`expected_output`|text||Expected output of program. Used when you want to compare with `stdout`.|`null`. Program's `stdout` won't be compared with `expected_output`.| 19 | |7|`cpu_time_limit`|float|second|Default runtime limit for every program. Time in which the OS assigns the processor to different tasks is not counted.|Depends on [configuration](#system-and-configuration-configuration-info).| 20 | |8|`cpu_extra_time`|float|second|When a time limit is exceeded, wait for extra time, before killing the program. This has the advantage that the real execution time is reported, even though it slightly exceeds the limit.|Depends on [configuration](#system-and-configuration-configuration-info).| 21 | |9|`wall_time_limit`|float|second|Limit wall-clock time in seconds. Decimal numbers are allowed. This clock measures the time from the start of the program to its exit, so it does not stop when the program has lost the CPU or when it is waiting for an external event. We recommend to use `cpu_time_limit` as the main limit, but set `wall_time_limit` to a much higher value as a precaution against sleeping programs.|Depends on [configuration](#system-and-configuration-configuration-info).| 22 | |10|`memory_limit`|float|kilobyte|Limit address space of the program.|Depends on [configuration](#system-and-configuration-configuration-info).| 23 | |11|`stack_limit`|integer|kilobyte|Limit process stack.|Depends on [configuration](#system-and-configuration-configuration-info).| 24 | |12|`max_processes_and_or_threads`|integer||Maximum number of processes and/or threads program can create.|Depends on [configuration](#system-and-configuration-configuration-info).| 25 | |13|`enable_per_process_and_thread_time_limit`|boolean||If `true` then `cpu_time_limit` will be used as per process and thread.|Depends on [configuration](#system-and-configuration-configuration-info).| 26 | |14|`enable_per_process_and_thread_memory_limit`|boolean||If `true` then `memory_limit` will be used as per process and thread.|Depends on [configuration](#system-and-configuration-configuration-info).| 27 | |15|`max_file_size`|integer|kilobyte|Limit file size created or modified by the program.|Depends on [configuration](#system-and-configuration-configuration-info).| 28 | |16|`number_of_runs`|integer||Run each program `number_of_runs` times and take average of `time` and `memory`.|Depends on [configuration](#system-and-configuration-configuration-info).| 29 | |17|`stdout`|text||Standard output of the program after execution.|| 30 | |18|`stderr`|text||Standard error of the program after execution.|| 31 | |19|`compile_output`|text||Compiler output after compilation.|| 32 | |20|`message`|text||If submission status is `Internal Error` then this message comes from Judge0 API itself, otherwise this is status message from Isolate.|| 33 | |21|`exit_code`|integer||The program's exit code.|| 34 | |22|`exit_signal`|integer||Signal code that the program recieved before exiting.|| 35 | |23|`status`|object||Submission [status](#statuses-and-languages-status).|| 36 | |24|`created_at`|datetime||Date and time when submission was created.|| 37 | |25|`finished_at`|datetime||Date and time when submission was processed.|`null` if submission is still in queue or if submission is processing.| 38 | |26|`token`|string||Unique submission token used for [getting specific submission](#submission-submission-get).|| 39 | |27|`time`|float|second|Program's runtime.|| 40 | |28|`wall_time`|float|second|Program's wall time. Will be greater or equal to `time`.|| 41 | |29|`memory`|float|kilobyte|Memory used by the program after execution.|| 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Judge0 API 2 | [![Judge0 API Wallpaper](https://github.com/judge0/api/blob/master/.github/wallpaper.png?raw=true)](https://api.judge0.com) 3 | 4 | [![License](https://img.shields.io/github/license/judge0/api?color=2185d0&style=flat-square)](https://github.com/judge0/api/blob/master/LICENSE) 5 | [![Release](https://img.shields.io/github/v/release/judge0/api?color=2185d0&style=flat-square)](https://github.com/judge0/api/releases) 6 | [![Stars](https://img.shields.io/github/stars/judge0/api?color=2185d0&style=flat-square)](https://github.com/judge0/api/stargazers) 7 | 8 | 9 | 10 | Donate with PayPal 11 | 12 | ## About 13 | [**Judge0 API**](https://api.judge0.com) is a free, robust and scalable open-source online code execution system. 14 | 15 | Judge0 API can help you build wide range of applications varying from competitive programming platforms, educational and recruitment platforms, to online code editors and more. 16 | 17 | To see Judge0 API in action, try [**Judge0 IDE**](https://ide.judge0.com) - free and open-source code editor that uses Judge0 API for executing the user’s source code. You can also try using a [dummy client](https://api.judge0.com/dummy-client.html), which can help you explore and test almost all the features of Judge0 API. 18 | 19 | ## Documentation 20 | Detailed API documentation can be found at https://api.judge0.com. 21 | 22 | ## Supported Languages 23 | List of supported languages for the latest release can be found [here](https://jsonpp.judge0.com/?https://api.judge0.com/languages). 24 | 25 | ## Installation 26 | To install Judge0 API on your server follow [deployment procedure](https://github.com/judge0/api/blob/master/CHANGELOG.md#deployment-procedure) for the latest release. 27 | 28 | ## Judge0 API in The World 29 | These projects and organizations successfully use Judge0 API: 30 | - [Adzetech](https://adzetech.com) 31 | - [CodeForum.ORG](https://codeforum.org) 32 | - [CodeInn](https://codeinn.org) 33 | - [CodeRunner](https://github.com/codeclassroom/CodeRunner) 34 | - [Codify](https://codify.herokuapp.com) 35 | - [Dilla University](http://www.duvlab.website) 36 | - [GeekCoders](http://www.geekcoders.co.in) 37 | - [Georg-August University of Göttingen](https://www.uni-goettingen.de) 38 | - [Institute of Aeronautical Engineering](https://www.iare.ac.in) 39 | - [Judge Code Online](http://en.chamcode.net) 40 | - [KPR Institute of Engineering and Technology](https://www.kpriet.ac.in) 41 | - [KodNest](https://www.kodnest.com) 42 | - [Newton School](https://www.newtonschool.co) 43 | - [SparkDatabox](https://sparkdatabox.com) 44 | - [Studyfied](https://studyfied.com) 45 | - [Techie Delight](https://techiedelight.com) 46 | - [Think exam](https://www.thinkexam.com) 47 | - [ThinkingBell](https://www.thinkingbell.com) 48 | - [University of Zagreb, Faculty of Electrical Engineering and Computing](https://www.fer.unizg.hr/en) 49 | - [Xiith](https://xiith.com) 50 | - [judge0api](https://github.com/vCra/judge0api) - Python library for interacting with Judge0 API 51 | - [nGrader](https://ngrader.herokuapp.com) 52 | - [primat.org](http://primat.org) 53 | - [wkspace](http://wkspace.herokuapp.com) 54 | 55 | Feel free to add your project or organization to the list. 56 | 57 | ## Community 58 | Do you have a question, feature request or something else on your mind? 59 | Or you just want to follow Judge0 news? 60 | Check out these links: 61 | 62 | * [Subscribe to Judge0 news](https://judge0.com/#subscribe) 63 | * [Join a Discord server](https://discord.gg/6dvxeA8) 64 | * [Report an issue](https://github.com/judge0/api/issues/new) 65 | * [Contact the author](https://github.com/hermanzdosilovic) 66 | 67 | ## Author and Contributors 68 | Judge0 API was created by [Herman Zvonimir Došilović](https://github.com/hermanzdosilovic). 69 | 70 | Thanks a lot to all [contributors](https://github.com/judge0/api/graphs/contributors) for their contributions in this project. 71 | 72 | ## Special Thanks 73 | Special thanks to these open-source projects without whom Judge0 API probably wouldn't exist: [isolate](https://github.com/ioi/isolate), [Docker](https://github.com/docker), [Ruby on Rails](https://github.com/rails/rails) and others. 74 | 75 | ## Supporters 76 | Thanks a lot to all my [Patrons](https://www.patreon.com/hermanzdosilovic) and [PayPal](https://paypal.me/hermanzdosilovic) donors that helped me keep Judge0 API free for everybody around the World. Thank you ♥ 77 | 78 | ## Donate 79 | Your are more than welcome to support Judge0 on [Patreon](https://www.patreon.com/hermanzdosilovic) or via [PayPal](https://paypal.me/hermanzdosilovic). Your monthly or one-time donation will help pay server costs and will improve future development and maintenance. Thank you ♥ 80 | 81 | ## License 82 | Judge0 API is licensed under the [GNU General Public License v3.0](https://github.com/judge0/api/blob/master/LICENSE). -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (5.0.7.2) 5 | actionpack (= 5.0.7.2) 6 | nio4r (>= 1.2, < 3.0) 7 | websocket-driver (~> 0.6.1) 8 | actionmailer (5.0.7.2) 9 | actionpack (= 5.0.7.2) 10 | actionview (= 5.0.7.2) 11 | activejob (= 5.0.7.2) 12 | mail (~> 2.5, >= 2.5.4) 13 | rails-dom-testing (~> 2.0) 14 | actionpack (5.0.7.2) 15 | actionview (= 5.0.7.2) 16 | activesupport (= 5.0.7.2) 17 | rack (~> 2.0) 18 | rack-test (~> 0.6.3) 19 | rails-dom-testing (~> 2.0) 20 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 21 | actionview (5.0.7.2) 22 | activesupport (= 5.0.7.2) 23 | builder (~> 3.1) 24 | erubis (~> 2.7.0) 25 | rails-dom-testing (~> 2.0) 26 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 27 | active_model_serializers (0.10.10) 28 | actionpack (>= 4.1, < 6.1) 29 | activemodel (>= 4.1, < 6.1) 30 | case_transform (>= 0.2) 31 | jsonapi-renderer (>= 0.1.1.beta1, < 0.3) 32 | activejob (5.0.7.2) 33 | activesupport (= 5.0.7.2) 34 | globalid (>= 0.3.6) 35 | activemodel (5.0.7.2) 36 | activesupport (= 5.0.7.2) 37 | activerecord (5.0.7.2) 38 | activemodel (= 5.0.7.2) 39 | activesupport (= 5.0.7.2) 40 | arel (~> 7.0) 41 | activesupport (5.0.7.2) 42 | concurrent-ruby (~> 1.0, >= 1.0.2) 43 | i18n (>= 0.7, < 2) 44 | minitest (~> 5.1) 45 | tzinfo (~> 1.1) 46 | annotate (3.0.3) 47 | activerecord (>= 3.2, < 7.0) 48 | rake (>= 10.4, < 14.0) 49 | arel (7.1.4) 50 | builder (3.2.4) 51 | byebug (11.0.1) 52 | case_transform (0.2) 53 | activesupport 54 | coderay (1.1.2) 55 | concurrent-ruby (1.1.5) 56 | crass (1.0.5) 57 | enumerations (2.3.3) 58 | activerecord 59 | activesupport 60 | i18n 61 | erubis (2.7.0) 62 | ffi (1.11.3) 63 | globalid (0.4.2) 64 | activesupport (>= 4.2.0) 65 | httparty (0.17.3) 66 | mime-types (~> 3.0) 67 | multi_xml (>= 0.5.2) 68 | i18n (1.8.1) 69 | concurrent-ruby (~> 1.0) 70 | jsonapi-renderer (0.2.2) 71 | listen (3.2.1) 72 | rb-fsevent (~> 0.10, >= 0.10.3) 73 | rb-inotify (~> 0.9, >= 0.9.10) 74 | loofah (2.4.0) 75 | crass (~> 1.0.2) 76 | nokogiri (>= 1.5.9) 77 | mail (2.7.1) 78 | mini_mime (>= 0.1.1) 79 | method_source (0.9.2) 80 | mime-types (3.3.1) 81 | mime-types-data (~> 3.2015) 82 | mime-types-data (3.2019.1009) 83 | mini_mime (1.0.2) 84 | mini_portile2 (2.4.0) 85 | minitest (5.13.0) 86 | mono_logger (1.1.0) 87 | multi_json (1.14.1) 88 | multi_xml (0.6.0) 89 | mustermann (1.1.1) 90 | ruby2_keywords (~> 0.0.1) 91 | nio4r (2.5.2) 92 | nokogiri (1.10.7) 93 | mini_portile2 (~> 2.4.0) 94 | pg (1.2.2) 95 | pry (0.12.2) 96 | coderay (~> 1.1.0) 97 | method_source (~> 0.9.0) 98 | pry-byebug (3.7.0) 99 | byebug (~> 11.0) 100 | pry (~> 0.10) 101 | pry-rails (0.3.9) 102 | pry (>= 0.10.4) 103 | puma (4.3.1) 104 | nio4r (~> 2.0) 105 | rack (2.1.1) 106 | rack-cors (1.1.1) 107 | rack (>= 2.0.0) 108 | rack-protection (2.0.8.1) 109 | rack 110 | rack-test (0.6.3) 111 | rack (>= 1.0) 112 | rails (5.0.7.2) 113 | actioncable (= 5.0.7.2) 114 | actionmailer (= 5.0.7.2) 115 | actionpack (= 5.0.7.2) 116 | actionview (= 5.0.7.2) 117 | activejob (= 5.0.7.2) 118 | activemodel (= 5.0.7.2) 119 | activerecord (= 5.0.7.2) 120 | activesupport (= 5.0.7.2) 121 | bundler (>= 1.3.0) 122 | railties (= 5.0.7.2) 123 | sprockets-rails (>= 2.0.0) 124 | rails-dom-testing (2.0.3) 125 | activesupport (>= 4.2.0) 126 | nokogiri (>= 1.6) 127 | rails-html-sanitizer (1.3.0) 128 | loofah (~> 2.3) 129 | railties (5.0.7.2) 130 | actionpack (= 5.0.7.2) 131 | activesupport (= 5.0.7.2) 132 | method_source 133 | rake (>= 0.8.7) 134 | thor (>= 0.18.1, < 2.0) 135 | rake (13.0.1) 136 | rb-fsevent (0.10.3) 137 | rb-inotify (0.10.1) 138 | ffi (~> 1.0) 139 | redis (4.1.3) 140 | redis-namespace (1.7.0) 141 | redis (>= 3.0.4) 142 | resque (2.0.0) 143 | mono_logger (~> 1.0) 144 | multi_json (~> 1.0) 145 | redis-namespace (~> 1.6) 146 | sinatra (>= 0.9.2) 147 | vegas (~> 0.1.2) 148 | ruby2_keywords (0.0.2) 149 | sinatra (2.0.8.1) 150 | mustermann (~> 1.0) 151 | rack (~> 2.0) 152 | rack-protection (= 2.0.8.1) 153 | tilt (~> 2.0) 154 | sprockets (4.0.0) 155 | concurrent-ruby (~> 1.0) 156 | rack (> 1, < 3) 157 | sprockets-rails (3.2.1) 158 | actionpack (>= 4.0) 159 | activesupport (>= 4.0) 160 | sprockets (>= 3.0.0) 161 | thor (1.0.1) 162 | thread_safe (0.3.6) 163 | tilt (2.0.10) 164 | tzinfo (1.2.6) 165 | thread_safe (~> 0.1) 166 | vegas (0.1.11) 167 | rack (>= 1.0.0) 168 | websocket-driver (0.6.5) 169 | websocket-extensions (>= 0.1.0) 170 | websocket-extensions (0.1.4) 171 | will_paginate (3.2.1) 172 | 173 | PLATFORMS 174 | ruby 175 | 176 | DEPENDENCIES 177 | active_model_serializers (~> 0.10.10) 178 | annotate (~> 3.0.3) 179 | enumerations (~> 2.3.3) 180 | httparty (~> 0.17.3) 181 | listen (~> 3.2.1) 182 | pg (~> 1.2.2) 183 | pry-byebug (~> 3.7.0) 184 | pry-rails (~> 0.3.9) 185 | puma (~> 4.3.1) 186 | rack-cors (~> 1.1.1) 187 | rails (~> 5.0.0) 188 | resque (~> 2.0.0) 189 | will_paginate (~> 3.2.1) 190 | 191 | BUNDLED WITH 192 | 2.1.4 193 | -------------------------------------------------------------------------------- /docs/api/submissions/create_a_submission.md: -------------------------------------------------------------------------------- 1 | ### Create a Submission [POST] 2 | Creates new submission. Created submission waits in queue to be processed. On successful 3 | creation, you are returned submission token which can be used to check submission status. 4 | 5 | If submission's `source_code`, `stdin` or `expected_output` contains non printable characters, or 6 | characters which cannot be sent with JSON, then set `base64_encoded` parameter to `true` and 7 | send these attributes Base64 encoded. Your responsibility is to encode each of mentioned attributes 8 | (`source_code`, `stdin` and `expected_output`) even if just one of them contains non printable 9 | characters. By default, this parameter is set to `false` and Judge0 API assumes you are sending plain text data. 10 | 11 | By default you are returned submission token on successful submission creation. With this token 12 | you can [check submission status](#submission-submission-get). 13 | Instead of checking submission status by making another request, send `wait` query parameter and set it to `true` 14 | which will enable you to get submission status immediately as part of response to the request you made. 15 | Please note that this feature may or may not be 16 | enabled on all Judge0 API hosts. So before using this feature please check [configuration](#system-and-configuration-configuration-info-get) 17 | of Judge0 API you are using. On an [official Judge0 API](https://api.judge0.com) this feature **is enabled**. 18 | 19 | + Parameters 20 | + base64_encoded = `false` (optional, boolean, `false`) ... Set to `true` if you want to send Base64 encoded data to Judge0 API. 21 | + wait = `false` (optional, boolean, `false`) ... Set to `true` to immediately get submission result. 22 | 23 | + Request (application/json) 24 | { 25 | "source_code": "#include \n\nint main(void) {\n char name[10];\n scanf(\"%s\", name);\n printf(\"hello, %s\\n\", name);\n return 0;\n}", 26 | "language_id": 4, 27 | "stdin": "world" 28 | } 29 | 30 | + Response 201 (application/json) 31 | { 32 | "token": "d85cd024-1548-4165-96c7-7bc88673f194" 33 | } 34 | 35 | 36 | + Request (application/json) 37 | { 38 | "source_code": "#include \n\nint main(void) {\n char name[10];\n scanf(\"%s\", name);\n printf(\"hello, %s\\n\", name);\n return 0;\n}" 39 | } 40 | 41 | + Response 422 (application/json) 42 | { 43 | "language_id": [ 44 | "can't be blank" 45 | ] 46 | } 47 | 48 | 49 | + Request (application/json) 50 | { 51 | "source_code": "#include \n\nint main(void) {\n char name[10];\n scanf(\"%s\", name);\n printf(\"hello, %s\\n\", name);\n return 0;\n}", 52 | "language_id": 150000, 53 | "stdin": "world", 54 | "expected_output": "hello, world" 55 | } 56 | 57 | + Response 422 (application/json) 58 | { 59 | "language_id": [ 60 | "language with id 150000 doesn't exist" 61 | ] 62 | } 63 | 64 | 65 | + Request (application/json) 66 | { 67 | "source_code": "#include \n\nint main(void) {\n char name[10];\n scanf(\"%s\", name);\n printf(\"hello, %s\\n\", name);\n return 0;\n}", 68 | "language_id": 4, 69 | "number_of_runs": 1, 70 | "stdin": "Judge0", 71 | "expected_output": "hello, Judge0", 72 | "cpu_time_limit": 1, 73 | "cpu_extra_time": 0.5, 74 | "wall_time_limit": 100000, 75 | "memory_limit": 128000, 76 | "stack_limit": 128000, 77 | "enable_per_process_and_thread_time_limit": false, 78 | "enable_per_process_and_thread_memory_limit": false, 79 | "max_file_size": 1024 80 | } 81 | 82 | + Response 422 (application/json) 83 | { 84 | "wall_time_limit": [ 85 | "must be less than or equal to 150" 86 | ] 87 | } 88 | 89 | 90 | + Request (appliction/json) 91 | Sending Base64 encoded `source_code` and `stdin`. Note that in this request `base64_encoded` query parameter **must** be 92 | set to `true`. 93 | + Body 94 | { 95 | "source_code": "I2luY2x1ZGUgPHN0ZGlvLmg+CgppbnQgbWFpbih2b2lkKSB7CiAgY2hhciBuYW1lWzEwXTsKICBzY2FuZigiJXMiLCBuYW1lKTsKICBwcmludGYoImhlbGxvLCAlc1xuIiwgbmFtZSk7CiAgcmV0dXJuIDA7Cn0=", 96 | "language_id": 4, 97 | "input": "SnVkZ2Uw" 98 | } 99 | 100 | + Response 201 (application/json) 101 | { 102 | "token": "f3fe0215-72f3-4fe6-97f5-353df6682db4" 103 | } 104 | 105 | + Request (application/json) 106 | Creating a submission with `wait=true` that results with one or more attributes that cannot be serialized to JSON without Base64 encoding. 107 | + Body 108 | { 109 | "language_id": 70, 110 | "source_code": "print(\"\\xFE\")" 111 | } 112 | 113 | + Response 201 (application/json) 114 | { 115 | "token": "fcd0de6d-ee52-4a9d-8a00-6e0d98d394cf", 116 | "error": "some attributes for this submission cannot be converted to UTF-8, use base64_encoded=true query parameter" 117 | } 118 | 119 | + Request (application/json) 120 | Waiting for submission to finish. Note that in this request `wait` query parameter **must** be set to `true`. 121 | + Body 122 | { 123 | "source_code": "#include \n\nint main(void) {\n char name[10];\n scanf(\"%s\", name);\n printf(\"hello, %s\\n\", name);\n return 0;\n}", 124 | "language_id": "4", 125 | "stdin": "Judge0", 126 | "expected_output": "hello, Judge0" 127 | } 128 | 129 | + Response 201 (application/json) 130 | { 131 | "stdout": "hello, Judge0\n", 132 | "time": "0.001", 133 | "memory": 380, 134 | "stderr": null, 135 | "token": "eb0dd001-66db-47f4-8a69-b736c9bc23f6", 136 | "compile_output": null, 137 | "message": null, 138 | "status": { 139 | "id": 3, 140 | "description": "Accepted" 141 | } 142 | } 143 | 144 | + Response 400 (application/json) 145 | If wait is not allowed. 146 | + Body 147 | { 148 | "error": "wait not allowed" 149 | } 150 | 151 | + Response 503 (application/json) 152 | If submission queue is full. 153 | + Body 154 | { 155 | "error": "queue is full" 156 | } 157 | 158 | 159 | -------------------------------------------------------------------------------- /docs/api/system_and_configuration/configuration_info.md: -------------------------------------------------------------------------------- 1 | ## Configuration Info [/config_info] 2 | ## Configuration Info [GET] 3 | Configuration information gives you detailed information about configuration of Judge0 API. 4 | This configuration can be changed through [judge0-api.conf](https://github.com/judge0/api/blob/master/judge0-api.conf.default) 5 | file by admin who hosts Judge0 API instance. 6 | 7 | This configuration gives every admin a flexibility to configure Judge0 API according to server abilities and needs. It also gives users 8 | insight on some *default configuration values* which are used when their programs are run. 9 | 10 | Each of these *configuration variables* have *default values* which we consider as recommended in case you are not sure should you change them. 11 | 12 | We will refer to *default values* as values which Judge0 API automatically assigns to each of these *configuration variables*, 13 | if admin didn't set them. For example, default value of *configuration variable* `cpu_time_limit` is `2`. 14 | 15 | |# |Name|Type |Unit |Description|Default Value| 16 | |:---:|:---|:---:|:---:|:----------|:------------| 17 | |1|`enable_wait_result`|boolean||If enabled user can request to synchronously wait for submission result on submission create.|true| 18 | |2|`enable_compiler_options`|boolean||If enabled user can set `compiler_options`.|true| 19 | |3|`allowed_languages_for_compile_options`|list of strings||Languages for which setting compiler options is allowed.|Empty, i.e. for all languages it is allowed to set compiler options.| 20 | |4|`enable_command_line_arguments`|boolean||If enabled user can set `command_line_arguments`.|true| 21 | |5|`enable_submission_delete`|boolean||If enabled authorized user can [delete a submission](#submissions-submission-delete).|false| 22 | |6|`max_queue_size`|integer||Maximum number of submissions that can wait in queue.|100| 23 | |7|`cpu_time_limit`|float|second|Default runtime limit for every program (in seconds). Decimal numbers are allowed. Time in which the OS assigns the processor to different tasks is not counted.|2| 24 | |8|`cpu_extra_time`|float|second|When a time limit is exceeded, wait for extra time, before killing the program. This has the advantage that the real execution time is reported, even though it slightly exceeds the limit.|0.5| 25 | |9|`wall_time_limit`|float|second|Limit wall-clock time in seconds. Decimal numbers are allowed. This clock measures the time from the start of the program to its exit, for an external event. We recommend to use `cpu_time_limit` as the main limit, but set `wall_time_limit` to a much higher value as a precaution against sleeping programs.|5| 26 | |10|`memory_limit`|integer|kilobyte|Limit address space of the program in kilobytes.|128000| 27 | |11|`stack_limit`|integer|kilobyte|Limit process stack in kilobytes.|64000| 28 | |12|`max_processes_and_or_threads`|integer||Maximum number of processes and/or threads program can create.|60| 29 | |13|`enable_per_process_and_thread_time_limit`|boolean||If `true` then `cpu_time_limit` will be used as per process and thread.|false| 30 | |14|`enable_per_process_and_thread_memory_limit`|boolean||If `true` then `memory_limit` will be used as per process and thread.|true| 31 | |15|`max_file_size`|integer|kilobyte|Limit size of files created (or modified) by the program.|1024| 32 | |16|`number_of_runs`|integer||Run each program this many times and take average of time and memory.|1| 33 | 34 | *Default configuration value* for each variable is given to you as response of this API call. For example, *default configuration value* 35 | for variable `cpu_extra_time` might be `2`, and if admin didn't set this, then it is `0.5` (*default value*). 36 | This means that admin set `cpu_extra_time` *configuration variable* to value `2` and we say it is now *default configuration value* for this 37 | variable `cpu_extra_time`. 38 | 39 | Every [submission](#submissions-submission) can change each of the configuration variables according to its needs. For example, 40 | user might create submission which has `cpu_time_limit` of `5` seconds. For security reasons we need to limit values of each of these 41 | configuration variables. For example, we don't want user to create a submission which has `cpu_time_limit` of `100000` seconds. 42 | 43 | For this security reason we are introducing *limit configuration variables* for each *configuration variable*. 44 | 45 | |# |Name|Type |Unit |Description|Default Value| 46 | |:---:|:---|:---:|:---:|:----------|:------------| 47 | |1|`max_cpu_time_limit`|float|second|Maximum custom `cpu_time_limit`|15| 48 | |2|`max_cpu_extra_time`|float|second|Maximum custom `cpu_extra_time`|2| 49 | |3|`max_wall_time_limit`|float|second|Maximum custom `wall_time_limit`|20| 50 | |4|`max_memory_limit`|integer|kilobyte|Maximum custom `memory_limit`|256000| 51 | |5|`max_stack_limit`|integer|kilobyte|Maximum custom `stack_limit`|128000| 52 | |6|`max_max_processes_and_or_threads`|integer||Maximum custom `max_processes_and_or_threads`|120| 53 | |7|`allow_enable_per_process_and_thread_time_limit`|boolean||If `false` user won't be able to set `enable_per_process_and_thread_time_limit` to `true`|true| 54 | |8|`allow_enable_per_process_and_thread_memory_limit`|boolean||If `false` user won't be able to set `enable_per_process_and_thread_memory_limit` to `true`|true| 55 | |9|`max_max_file_size`|integer|kilobyte|Maximux custom `max_file_size`|4096| 56 | |10|`max_number_of_runs`|integer||Maximum custom `number_of_runs`|20| 57 | 58 | For example, `max_cpu_time_limit` with value `20` means that user cannot create new submission which has `cpu_time_limit` greater than `20`. 59 | 60 | + Response 200 (application/json) 61 | { 62 | "enable_wait_result": true, 63 | "enable_compiler_options": true, 64 | "allowed_languages_for_compile_options": [], 65 | "enable_command_line_arguments": true, 66 | "enable_submission_delete": false, 67 | "max_queue_size": 100, 68 | "cpu_time_limit": 2, 69 | "max_cpu_time_limit": 15, 70 | "cpu_extra_time": 0.5, 71 | "max_cpu_extra_time": 2, 72 | "wall_time_limit": 5, 73 | "max_wall_time_limit": 20, 74 | "memory_limit": 128000, 75 | "max_memory_limit": 256000, 76 | "stack_limit": 64000, 77 | "max_stack_limit": 128000, 78 | "max_processes_and_or_threads": 60, 79 | "max_max_processes_and_or_threads": 120, 80 | "enable_per_process_and_thread_time_limit": false, 81 | "allow_enable_per_process_and_thread_time_limit": true, 82 | "enable_per_process_and_thread_memory_limit": true, 83 | "allow_enable_per_process_and_thread_memory_limit": true, 84 | "max_file_size": 1024, 85 | "max_max_file_size": 4096, 86 | "number_of_runs": 1, 87 | "max_number_of_runs": 20 88 | } -------------------------------------------------------------------------------- /db/languages/active.rb: -------------------------------------------------------------------------------- 1 | @languages ||= [] 2 | @languages += 3 | [ 4 | { 5 | id: 43, 6 | name: "Plain Text", 7 | is_archived: false, 8 | source_file: "text.txt", 9 | run_cmd: "/bin/cat text.txt" 10 | }, 11 | { 12 | id: 44, 13 | name: "Executable", 14 | is_archived: false, 15 | source_file: "a.out", 16 | run_cmd: "/bin/chmod +x a.out && ./a.out" 17 | }, 18 | { 19 | id: 45, 20 | name: "Assembly (NASM 2.14.02)", 21 | is_archived: false, 22 | source_file: "main.asm", 23 | compile_cmd: "/usr/local/nasm-2.14.02/bin/nasmld -f elf64 %s main.asm", 24 | run_cmd: "./a.out" 25 | }, 26 | { 27 | id: 46, 28 | name: "Bash (5.0.0)", 29 | is_archived: false, 30 | source_file: "script.sh", 31 | run_cmd: "/usr/local/bash-5.0/bin/bash script.sh" 32 | }, 33 | { 34 | id: 47, 35 | name: "Basic (FBC 1.07.1)", 36 | is_archived: false, 37 | source_file: "main.bas", 38 | compile_cmd: "/usr/local/fbc-1.07.1/bin/fbc %s main.bas", 39 | run_cmd: "./main" 40 | }, 41 | { 42 | id: 48, 43 | name: "C (GCC 7.4.0)", 44 | is_archived: false, 45 | source_file: "main.c", 46 | compile_cmd: "/usr/local/gcc-7.4.0/bin/gcc %s main.c", 47 | run_cmd: "./a.out" 48 | }, 49 | { 50 | id: 49, 51 | name: "C (GCC 8.3.0)", 52 | is_archived: false, 53 | source_file: "main.c", 54 | compile_cmd: "/usr/local/gcc-8.3.0/bin/gcc %s main.c", 55 | run_cmd: "./a.out" 56 | }, 57 | { 58 | id: 50, 59 | name: "C (GCC 9.2.0)", 60 | is_archived: false, 61 | source_file: "main.c", 62 | compile_cmd: "/usr/local/gcc-9.2.0/bin/gcc %s main.c", 63 | run_cmd: "./a.out" 64 | }, 65 | { 66 | id: 51, 67 | name: "C# (Mono 6.6.0.161)", 68 | is_archived: false, 69 | source_file: "Main.cs", 70 | compile_cmd: "/usr/local/mono-6.6.0.161/bin/mcs %s Main.cs", 71 | run_cmd: "/usr/local/mono-6.6.0.161/bin/mono Main.exe" 72 | }, 73 | { 74 | id: 52, 75 | name: "C++ (GCC 7.4.0)", 76 | is_archived: false, 77 | source_file: "main.cpp", 78 | compile_cmd: "/usr/local/gcc-7.4.0/bin/g++ %s main.cpp", 79 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-7.4.0/lib64 ./a.out" 80 | }, 81 | { 82 | id: 53, 83 | name: "C++ (GCC 8.3.0)", 84 | is_archived: false, 85 | source_file: "main.cpp", 86 | compile_cmd: "/usr/local/gcc-8.3.0/bin/g++ %s main.cpp", 87 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-8.3.0/lib64 ./a.out" 88 | }, 89 | { 90 | id: 54, 91 | name: "C++ (GCC 9.2.0)", 92 | is_archived: false, 93 | source_file: "main.cpp", 94 | compile_cmd: "/usr/local/gcc-9.2.0/bin/g++ %s main.cpp", 95 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-9.2.0/lib64 ./a.out" 96 | }, 97 | { 98 | id: 55, 99 | name: "Common Lisp (SBCL 2.0.0)", 100 | is_archived: false, 101 | source_file: "script.lisp", 102 | run_cmd: "SBCL_HOME=/usr/local/sbcl-2.0.0/lib/sbcl /usr/local/sbcl-2.0.0/bin/sbcl --script script.lisp" 103 | }, 104 | { 105 | id: 56, 106 | name: "D (DMD 2.089.1)", 107 | is_archived: false, 108 | source_file: "main.d", 109 | compile_cmd: "/usr/local/d-2.089.1/linux/bin64/dmd %s main.d", 110 | run_cmd: "./main" 111 | }, 112 | { 113 | id: 57, 114 | name: "Elixir (1.9.4)", 115 | is_archived: false, 116 | source_file: "script.exs", 117 | run_cmd: "/usr/local/elixir-1.9.4/bin/elixir script.exs" 118 | }, 119 | { 120 | id: 58, 121 | name: "Erlang (OTP 22.2)", 122 | is_archived: false, 123 | source_file: "main.erl", 124 | run_cmd: "/bin/sed -i '1s/^/\\n/' main.erl && /usr/local/erlang-22.2/bin/escript main.erl" 125 | }, 126 | { 127 | id: 59, 128 | name: "Fortran (GFortran 9.2.0)", 129 | is_archived: false, 130 | source_file: "main.f90", 131 | compile_cmd: "/usr/local/gcc-9.2.0/bin/gfortran %s main.f90", 132 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-9.2.0/lib64 ./a.out" 133 | }, 134 | { 135 | id: 60, 136 | name: "Go (1.13.5)", 137 | is_archived: false, 138 | source_file: "main.go", 139 | compile_cmd: "GOCACHE=/tmp/.cache/go-build /usr/local/go-1.13.5/bin/go build %s main.go", 140 | run_cmd: "./main" 141 | }, 142 | { 143 | id: 61, 144 | name: "Haskell (GHC 8.8.1)", 145 | is_archived: false, 146 | source_file: "main.hs", 147 | compile_cmd: "/usr/local/ghc-8.8.1/bin/ghc %s main.hs", 148 | run_cmd: "./main" 149 | }, 150 | { 151 | id: 62, 152 | name: "Java (OpenJDK 13.0.1)", 153 | is_archived: false, 154 | source_file: "Main.java", 155 | compile_cmd: "/usr/local/openjdk13/bin/javac %s Main.java", 156 | run_cmd: "/usr/local/openjdk13/bin/java Main" 157 | }, 158 | { 159 | id: 63, 160 | name: "JavaScript (Node.js 12.14.0)", 161 | is_archived: false, 162 | source_file: "script.js", 163 | run_cmd: "/usr/local/node-12.14.0/bin/node script.js" 164 | }, 165 | { 166 | id: 64, 167 | name: "Lua (5.3.5)", 168 | is_archived: false, 169 | source_file: "script.lua", 170 | compile_cmd: "/usr/local/lua-5.3.5/luac53 %s script.lua", 171 | run_cmd: "/usr/local/lua-5.3.5/lua53 ./luac.out" 172 | }, 173 | { 174 | id: 65, 175 | name: "OCaml (4.09.0)", 176 | is_archived: false, 177 | source_file: "main.ml", 178 | compile_cmd: "/usr/local/ocaml-4.09.0/bin/ocamlc %s main.ml", 179 | run_cmd: "./a.out" 180 | }, 181 | { 182 | id: 66, 183 | name: "Octave (5.1.0)", 184 | is_archived: false, 185 | source_file: "script.m", 186 | run_cmd: "/usr/local/octave-5.1.0/bin/octave-cli -q --no-gui --no-history script.m" 187 | }, 188 | { 189 | id: 67, 190 | name: "Pascal (FPC 3.0.4)", 191 | is_archived: false, 192 | source_file: "main.pas", 193 | compile_cmd: "/usr/local/fpc-3.0.4/bin/fpc %s main.pas", 194 | run_cmd: "./main" 195 | }, 196 | { 197 | id: 68, 198 | name: "PHP (7.4.1)", 199 | is_archived: false, 200 | source_file: "script.php", 201 | run_cmd: "/usr/local/php-7.4.1/bin/php script.php" 202 | }, 203 | { 204 | id: 69, 205 | name: "Prolog (GNU Prolog 1.4.5)", 206 | is_archived: false, 207 | source_file: "main.pro", 208 | compile_cmd: "PATH=\"/usr/local/gprolog-1.4.5/gprolog-1.4.5/bin:$PATH\" /usr/local/gprolog-1.4.5/gprolog-1.4.5/bin/gplc --no-top-level %s main.pro", 209 | run_cmd: "./main" 210 | }, 211 | { 212 | id: 70, 213 | name: "Python (2.7.17)", 214 | is_archived: false, 215 | source_file: "script.py", 216 | run_cmd: "/usr/local/python-2.7.17/bin/python2 script.py" 217 | }, 218 | { 219 | id: 71, 220 | name: "Python (3.8.1)", 221 | is_archived: false, 222 | source_file: "script.py", 223 | run_cmd: "/usr/local/python-3.8.1/bin/python3 script.py" 224 | }, 225 | { 226 | id: 72, 227 | name: "Ruby (2.7.0)", 228 | is_archived: false, 229 | source_file: "script.rb", 230 | run_cmd: "/usr/local/ruby-2.7.0/bin/ruby script.rb" 231 | }, 232 | { 233 | id: 73, 234 | name: "Rust (1.40.0)", 235 | is_archived: false, 236 | source_file: "main.rs", 237 | compile_cmd: "/usr/local/rust-1.40.0/bin/rustc %s main.rs", 238 | run_cmd: "./main" 239 | }, 240 | { 241 | id: 74, 242 | name: "TypeScript (3.7.4)", 243 | is_archived: false, 244 | source_file: "script.ts", 245 | compile_cmd: "/usr/bin/tsc %s script.ts", 246 | run_cmd: "/usr/local/node-12.14.0/bin/node script.js" 247 | } 248 | ] -------------------------------------------------------------------------------- /app/controllers/submissions_controller.rb: -------------------------------------------------------------------------------- 1 | class SubmissionsController < ApplicationController 2 | before_action :authorize_request, only: [:index, :destroy] 3 | before_action :check_maintenance, only: [:create, :destroy] 4 | before_action :check_wait, only: [:create] # Wait in batch_create is not allowed 5 | before_action :check_queue_size, only: [:create, :batch_create] 6 | before_action :check_requested_fields, except: [:batch_create] # Fields are ignored in batch_create 7 | before_action :set_base64_encoded 8 | 9 | def index 10 | page = params[:page].try(:to_i) || 1 11 | per_page = params[:per_page].try(:to_i) || Submission.per_page 12 | 13 | if page <= 0 14 | render json: { error: "invalid page: #{page}" }, status: :bad_request 15 | return 16 | elsif per_page < 0 17 | render json: { error: "invalid per_page: #{per_page}" }, status: :bad_request 18 | return 19 | end 20 | 21 | submissions = Submission.paginate(page: page, per_page: per_page) 22 | serializable_submissions = ActiveModelSerializers::SerializableResource.new( 23 | submissions, { each_serializer: SubmissionSerializer, base64_encoded: @base64_encoded, fields: @requested_fields } 24 | ) 25 | 26 | render json: { 27 | submissions: serializable_submissions.as_json, 28 | meta: pagination_dict(submissions) 29 | } 30 | rescue Encoding::UndefinedConversionError => e 31 | render json: { 32 | error: "some attributes for one or more submissions cannot be converted to UTF-8, use base64_encoded=true query parameter" 33 | }, status: :bad_request 34 | end 35 | 36 | def destroy 37 | if !Config::ENABLE_SUBMISSION_DELETE 38 | render json: { error: "delete not allowed" }, status: :bad_request 39 | return 40 | end 41 | 42 | submission = Submission.find_by!(token: params[:token]) 43 | if submission.status == Status.queue || submission.status == Status.process 44 | render json: { 45 | error: "submission cannot be deleted because its status is #{submission.status.id} (#{submission.status.name})" 46 | }, status: :bad_request 47 | return 48 | end 49 | 50 | submission.delete 51 | 52 | # Forcing base64_encoded=true because it guarantees user will get requested data after delete. 53 | render json: submission, base64_encoded: true, fields: @requested_fields 54 | end 55 | 56 | def show 57 | render json: Submission.find_by!(token: params[:token]), base64_encoded: @base64_encoded, fields: @requested_fields 58 | rescue Encoding::UndefinedConversionError 59 | render_conversion_error(:bad_request) 60 | end 61 | 62 | def batch_show 63 | tokens = (request.headers[:tokens] || params[:tokens]).to_s.strip.split(",") 64 | 65 | if tokens.length > Config::MAX_SUBMISSION_BATCH_SIZE 66 | render json: { 67 | error: "number of submissions in a batch should be less than or equal to #{Config::MAX_SUBMISSION_BATCH_SIZE}" 68 | }, status: :bad_request 69 | return 70 | elsif tokens.length == 0 71 | render json: { 72 | error: "there should be at least one submission in a batch" 73 | }, status: :bad_request 74 | return 75 | end 76 | 77 | existing_submissions = Hash[Submission.where(token: tokens).collect{ |s| [s.token, s] }] 78 | 79 | submissions = [] 80 | tokens.each do |token| 81 | if existing_submissions.has_key?(token) 82 | serialized_submission = ActiveModelSerializers::SerializableResource.new( 83 | existing_submissions[token], { serializer: SubmissionSerializer, base64_encoded: @base64_encoded, fields: @requested_fields } 84 | ) 85 | submissions << serialized_submission.as_json 86 | else 87 | submissions << nil 88 | end 89 | end 90 | 91 | render json: { submissions: submissions } 92 | rescue Encoding::UndefinedConversionError => e 93 | render json: { 94 | error: "some attributes for one or more submissions cannot be converted to UTF-8, use base64_encoded=true query parameter" 95 | }, status: :bad_request 96 | end 97 | 98 | def create 99 | submission = Submission.new(submission_params(params)) 100 | 101 | if submission.save 102 | if @wait 103 | begin 104 | IsolateJob.perform_now(submission) 105 | render json: submission, status: :created, base64_encoded: @base64_encoded, fields: @requested_fields 106 | rescue Encoding::UndefinedConversionError => e 107 | render_conversion_error(:created, submission.token) 108 | end 109 | else 110 | IsolateJob.perform_later(submission) 111 | render json: submission, status: :created, fields: [:token] 112 | end 113 | else 114 | render json: submission.errors, status: :unprocessable_entity 115 | end 116 | end 117 | 118 | def batch_create 119 | number_of_submissions = params[:submissions].try(:size).to_i 120 | 121 | if number_of_submissions > Config::MAX_SUBMISSION_BATCH_SIZE 122 | render json: { 123 | error: "number of submissions in a batch should be less than or equal to #{Config::MAX_SUBMISSION_BATCH_SIZE}" 124 | }, status: :bad_request 125 | return 126 | elsif number_of_submissions == 0 127 | render json: { 128 | error: "there should be at least one submission in a batch" 129 | }, status: :bad_request 130 | return 131 | end 132 | 133 | submissions = params[:submissions].each.collect{ |p| Submission.new(submission_params(p)) } 134 | 135 | response = [] 136 | has_valid_submission = false 137 | 138 | submissions.each do |submission| 139 | if submission.save 140 | IsolateJob.perform_later(submission) 141 | response << { token: submission.token } 142 | has_valid_submission = true 143 | else 144 | response << submission.errors 145 | end 146 | end 147 | 148 | render json: response, status: has_valid_submission ? :created : :unprocessable_entity 149 | end 150 | 151 | private 152 | 153 | def submission_params(params) 154 | submission_params = params.permit( 155 | :source_code, 156 | :language_id, 157 | :compiler_options, 158 | :command_line_arguments, 159 | :number_of_runs, 160 | :stdin, 161 | :expected_output, 162 | :cpu_time_limit, 163 | :cpu_extra_time, 164 | :wall_time_limit, 165 | :memory_limit, 166 | :stack_limit, 167 | :max_processes_and_or_threads, 168 | :enable_per_process_and_thread_time_limit, 169 | :enable_per_process_and_thread_memory_limit, 170 | :max_file_size, 171 | :redirect_stderr_to_stdout, 172 | :callback_url, 173 | :archive 174 | ) 175 | 176 | submission_params[:archive] = Base64Service.decode(submission_params[:archive]) 177 | 178 | if @base64_encoded 179 | submission_params[:source_code] = Base64Service.decode(submission_params[:source_code]) 180 | submission_params[:stdin] = Base64Service.decode(submission_params[:stdin]) 181 | submission_params[:expected_output] = Base64Service.decode(submission_params[:expected_output]) 182 | end 183 | 184 | submission_params 185 | end 186 | 187 | def check_wait 188 | @wait = params[:wait] == "true" 189 | if @wait && !Config::ENABLE_WAIT_RESULT 190 | render json: { error: "wait not allowed" }, status: :bad_request 191 | end 192 | end 193 | 194 | def check_queue_size 195 | number_of_submissions = params[:submissions].try(:size).presence || 1 196 | if Resque.size(ENV["JUDGE0_VERSION"]) + number_of_submissions > Config::MAX_QUEUE_SIZE 197 | render json: { error: "queue is full" }, status: :service_unavailable 198 | end 199 | end 200 | 201 | def check_requested_fields 202 | fields_service = Fields::Submission.new(params[:fields]) 203 | render json: { error: "invalid fields: [#{fields_service.invalid_fields.join(", ")}]" }, status: :bad_request if fields_service.has_invalid_fields? 204 | @requested_fields = fields_service.requested_fields 205 | end 206 | 207 | def set_base64_encoded 208 | @base64_encoded = params[:base64_encoded] == "true" 209 | end 210 | 211 | def render_conversion_error(status, token = nil) 212 | response_json = { 213 | error: "some attributes for this submission cannot be converted to UTF-8, use base64_encoded=true query parameter", 214 | } 215 | response_json[:token] = token if token 216 | 217 | render json: response_json, status: status 218 | end 219 | end 220 | -------------------------------------------------------------------------------- /app/models/submission.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: submissions 4 | # 5 | # id :integer not null, primary key 6 | # source_code :text 7 | # language_id :integer 8 | # stdin :text 9 | # expected_output :text 10 | # stdout :text 11 | # status_id :integer 12 | # created_at :datetime 13 | # finished_at :datetime 14 | # time :decimal(, ) 15 | # memory :integer 16 | # stderr :text 17 | # token :string 18 | # number_of_runs :integer 19 | # cpu_time_limit :decimal(, ) 20 | # cpu_extra_time :decimal(, ) 21 | # wall_time_limit :decimal(, ) 22 | # memory_limit :integer 23 | # stack_limit :integer 24 | # max_processes_and_or_threads :integer 25 | # enable_per_process_and_thread_time_limit :boolean 26 | # enable_per_process_and_thread_memory_limit :boolean 27 | # max_file_size :integer 28 | # compile_output :text 29 | # exit_code :integer 30 | # exit_signal :integer 31 | # message :text 32 | # wall_time :decimal(, ) 33 | # compiler_options :string 34 | # command_line_arguments :string 35 | # 36 | # Indexes 37 | # 38 | # index_submissions_on_token (token) 39 | # 40 | 41 | class Submission < ApplicationRecord 42 | validates :source_code, :language_id, presence: true 43 | validates :number_of_runs, 44 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_NUMBER_OF_RUNS } 45 | validates :cpu_time_limit, 46 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_CPU_TIME_LIMIT } 47 | validates :cpu_extra_time, 48 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_CPU_EXTRA_TIME } 49 | validates :wall_time_limit, 50 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_WALL_TIME_LIMIT } 51 | validates :memory_limit, 52 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_MEMORY_LIMIT } 53 | validates :stack_limit, 54 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_STACK_LIMIT } 55 | validates :max_processes_and_or_threads, 56 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_MAX_PROCESSES_AND_OR_THREADS } 57 | validates :enable_per_process_and_thread_time_limit, 58 | inclusion: { in: [false], message: "this option cannot be enabled" }, 59 | unless: "Config::ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT" 60 | validates :enable_per_process_and_thread_memory_limit, 61 | inclusion: { in: [false], message: "this option cannot be enabled" }, 62 | unless: "Config::ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT" 63 | validates :max_file_size, 64 | numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_MAX_FILE_SIZE } 65 | validates :compiler_options, length: { maximum: 512 } 66 | validates :command_line_arguments, length: { maximum: 512 } 67 | validate :language_existence, :compiler_options_allowed, :command_line_arguments_allowed 68 | 69 | before_create :generate_token 70 | before_validation :set_defaults 71 | 72 | enumeration :status 73 | 74 | default_scope { order(created_at: :desc) } 75 | 76 | self.per_page = 20 77 | 78 | def source_code 79 | @decoded_source_code ||= Base64Service.decode(self[:source_code]) 80 | end 81 | 82 | def source_code=(value) 83 | super(value) 84 | self[:source_code] = Base64Service.encode(self[:source_code]) 85 | end 86 | 87 | 88 | def stdin 89 | @decoded_stdin ||= Base64Service.decode(self[:stdin]) 90 | end 91 | 92 | def stdin=(value) 93 | super(value) 94 | self[:stdin] = Base64Service.encode(self[:stdin]) 95 | end 96 | 97 | 98 | def stdout 99 | @decoded_stdout ||= Base64Service.decode(self[:stdout]) 100 | end 101 | 102 | def stdout=(value) 103 | super(value) 104 | self[:stdout] = Base64Service.encode(self[:stdout]) 105 | end 106 | 107 | 108 | def expected_output 109 | @decoded_expected_output ||= Base64Service.decode(self[:expected_output]) 110 | end 111 | 112 | def expected_output=(value) 113 | super(value) 114 | self[:expected_output] = Base64Service.encode(self[:expected_output]) 115 | end 116 | 117 | 118 | def stderr 119 | @decoded_stderr ||= Base64Service.decode(self[:stderr]) 120 | end 121 | 122 | def stderr=(value) 123 | super(value) 124 | self[:stderr] = Base64Service.encode(self[:stderr]) 125 | end 126 | 127 | 128 | def compile_output 129 | @decoded_compile_output ||= Base64Service.decode(self[:compile_output]) 130 | end 131 | 132 | def compile_output=(value) 133 | super(value) 134 | self[:compile_output] = Base64Service.encode(self[:compile_output]) 135 | end 136 | 137 | 138 | def language 139 | @language ||= Language.unscoped.find(language_id) 140 | end 141 | 142 | 143 | def status 144 | Status.find_by(id: status_id) 145 | end 146 | 147 | def status=(status) 148 | self.status_id = status.id 149 | end 150 | 151 | private 152 | 153 | def language_existence 154 | if language_id && !Language.unscoped.exists?(language_id) 155 | errors.add(:language_id, "language with id #{language_id} doesn't exist") 156 | elsif language_id && Language.unscoped.find(language_id).try(:is_archived) 157 | errors.add(:language_id, "language with id #{language_id} is archived and cannot be used anymore") 158 | end 159 | end 160 | 161 | def compiler_options_allowed 162 | return if compiler_options.blank? 163 | 164 | unless Config::ENABLE_COMPILER_OPTIONS 165 | errors.add(:compiler_options, "setting compiler options is not allowed") 166 | return 167 | end 168 | 169 | if Language.unscoped.exists?(language_id) && language.compile_cmd.nil? 170 | errors.add(:compiler_options, "setting compiler options is only allowed for compiled languages") 171 | return 172 | end 173 | 174 | @@allowed_languages ||= Config::ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS.collect{ |s| s + " " } 175 | if Language.unscoped.exists?(language_id) && @@allowed_languages.present? && !language.name.starts_with?(*@@allowed_languages) 176 | @@allowed_languages_message ||= @@allowed_languages.size > 1 ? @@allowed_languages[0..-2].collect{ |s| s.strip }.join(", ") + " and " + @@allowed_languages[-1].strip : @@allowed_languages[0].strip 177 | errors.add(:compiler_options, "setting compiler options is only allowed for #{@@allowed_languages_message}") 178 | end 179 | end 180 | 181 | def command_line_arguments_allowed 182 | return if command_line_arguments.blank? 183 | 184 | unless Config::ENABLE_COMMAND_LINE_ARGUMENTS 185 | errors.add(:command_line_arguments, "setting command line arguments is not allowed") 186 | end 187 | end 188 | 189 | def generate_token 190 | begin 191 | self.token = SecureRandom.uuid 192 | end while self.class.exists?(token: token) 193 | end 194 | 195 | def set_defaults 196 | self.status ||= Status.queue 197 | self.number_of_runs ||= Config::NUMBER_OF_RUNS 198 | self.cpu_time_limit ||= Config::CPU_TIME_LIMIT 199 | self.cpu_extra_time ||= Config::CPU_EXTRA_TIME 200 | self.wall_time_limit ||= Config::WALL_TIME_LIMIT 201 | self.memory_limit ||= Config::MEMORY_LIMIT 202 | self.stack_limit ||= Config::STACK_LIMIT 203 | self.max_processes_and_or_threads ||= Config::MAX_PROCESSES_AND_OR_THREADS 204 | self.enable_per_process_and_thread_time_limit = NilValue.value_or_default( 205 | self.enable_per_process_and_thread_time_limit, 206 | Config::ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT 207 | ) 208 | self.enable_per_process_and_thread_memory_limit = NilValue.value_or_default( 209 | self.enable_per_process_and_thread_memory_limit, 210 | Config::ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT 211 | ) 212 | self.max_file_size ||= Config::MAX_FILE_SIZE 213 | self.redirect_stderr_to_stdout = NilValue.value_or_default( 214 | self.redirect_stderr_to_stdout, 215 | Config::REDIRECT_STDERR_TO_STDOUT 216 | ) 217 | end 218 | end 219 | -------------------------------------------------------------------------------- /db/languages/archived.rb: -------------------------------------------------------------------------------- 1 | @languages ||= [] 2 | @languages += 3 | [ 4 | { 5 | id: 1, 6 | name: "Bash (4.4)", 7 | is_archived: true, 8 | source_file: "script.sh", 9 | run_cmd: "/usr/local/bash-4.4/bin/bash script.sh" 10 | }, 11 | { 12 | id: 2, 13 | name: "Bash (4.0)", 14 | is_archived: true, 15 | source_file: "script.sh", 16 | run_cmd: "/usr/local/bash-4.0/bin/bash script.sh" 17 | }, 18 | { 19 | id: 3, 20 | name: "Basic (fbc 1.05.0)", 21 | is_archived: true, 22 | source_file: "main.bas", 23 | compile_cmd: "/usr/local/fbc-1.05.0/bin/fbc %s main.bas", 24 | run_cmd: "./main" 25 | }, 26 | { 27 | id: 4, 28 | name: "C (gcc 7.2.0)", 29 | is_archived: true, 30 | source_file: "main.c", 31 | compile_cmd: "/usr/local/gcc-7.2.0/bin/gcc %s main.c", 32 | run_cmd: "./a.out" 33 | }, 34 | { 35 | id: 5, 36 | name: "C (gcc 6.4.0)", 37 | is_archived: true, 38 | source_file: "main.c", 39 | compile_cmd: "/usr/local/gcc-6.4.0/bin/gcc %s main.c", 40 | run_cmd: "./a.out" 41 | }, 42 | { 43 | id: 6, 44 | name: "C (gcc 6.3.0)", 45 | is_archived: true, 46 | source_file: "main.c", 47 | compile_cmd: "/usr/local/gcc-6.3.0/bin/gcc %s main.c", 48 | run_cmd: "./a.out" 49 | }, 50 | { 51 | id: 7, 52 | name: "C (gcc 5.4.0)", 53 | is_archived: true, 54 | source_file: "main.c", 55 | compile_cmd: "/usr/local/gcc-5.4.0/bin/gcc %s main.c", 56 | run_cmd: "./a.out" 57 | }, 58 | { 59 | id: 8, 60 | name: "C (gcc 4.9.4)", 61 | is_archived: true, 62 | source_file: "main.c", 63 | compile_cmd: "/usr/local/gcc-4.9.4/bin/gcc %s main.c", 64 | run_cmd: "./a.out" 65 | }, 66 | { 67 | id: 9, 68 | name: "C (gcc 4.8.5)", 69 | is_archived: true, 70 | source_file: "main.c", 71 | compile_cmd: "/usr/local/gcc-4.8.5/bin/gcc %s main.c", 72 | run_cmd: "./a.out" 73 | }, 74 | 75 | { 76 | id: 10, 77 | name: "C++ (g++ 7.2.0)", 78 | is_archived: true, 79 | source_file: "main.cpp", 80 | compile_cmd: "/usr/local/gcc-7.2.0/bin/g++ %s main.cpp", 81 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-7.2.0/lib64 ./a.out" 82 | }, 83 | { 84 | id: 11, 85 | name: "C++ (g++ 6.4.0)", 86 | is_archived: true, 87 | source_file: "main.cpp", 88 | compile_cmd: "/usr/local/gcc-6.4.0/bin/g++ %s main.cpp", 89 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-6.4.0/lib64 ./a.out" 90 | }, 91 | { 92 | id: 12, 93 | name: "C++ (g++ 6.3.0)", 94 | is_archived: true, 95 | source_file: "main.cpp", 96 | compile_cmd: "/usr/local/gcc-6.3.0/bin/g++ %s main.cpp", 97 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-6.3.0/lib64 ./a.out" 98 | }, 99 | { 100 | id: 13, 101 | name: "C++ (g++ 5.4.0)", 102 | is_archived: true, 103 | source_file: "main.cpp", 104 | compile_cmd: "/usr/local/gcc-5.4.0/bin/g++ %s main.cpp", 105 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-5.4.0/lib64 ./a.out" 106 | }, 107 | { 108 | id: 14, 109 | name: "C++ (g++ 4.9.4)", 110 | is_archived: true, 111 | source_file: "main.cpp", 112 | compile_cmd: "/usr/local/gcc-4.9.4/bin/g++ %s main.cpp", 113 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-4.9.4/lib64 ./a.out" 114 | }, 115 | { 116 | id: 15, 117 | name: "C++ (g++ 4.8.5)", 118 | is_archived: true, 119 | source_file: "main.cpp", 120 | compile_cmd: "/usr/local/gcc-4.8.5/bin/g++ %s main.cpp", 121 | run_cmd: "LD_LIBRARY_PATH=/usr/local/gcc-4.8.5/lib64 ./a.out" 122 | }, 123 | { 124 | id: 16, 125 | name: "C# (mono 5.4.0.167)", 126 | is_archived: true, 127 | source_file: "Main.cs", 128 | compile_cmd: "/usr/local/mono-5.4.0.167/bin/mcs %s Main.cs", 129 | run_cmd: "/usr/local/mono-5.4.0.167/bin/mono Main.exe" 130 | }, 131 | { 132 | id: 17, 133 | name: "C# (mono 5.2.0.224)", 134 | is_archived: true, 135 | source_file: "Main.cs", 136 | compile_cmd: "/usr/local/mono-5.2.0.224/bin/mcs %s Main.cs", 137 | run_cmd: "/usr/local/mono-5.2.0.224/bin/mono Main.exe" 138 | }, 139 | { 140 | id: 18, 141 | name: "Clojure (1.8.0)", 142 | is_archived: true, 143 | source_file: "main.clj", 144 | run_cmd: "/usr/bin/java -cp /usr/local/clojure-1.8.0/clojure-1.8.0.jar clojure.main main.clj" 145 | }, 146 | { 147 | id: 19, 148 | name: "Crystal (0.23.1)", 149 | is_archived: true, 150 | source_file: "main.cr", 151 | compile_cmd: "/usr/local/crystal-0.23.1-3/bin/crystal build %s main.cr", 152 | run_cmd: "./main" 153 | }, 154 | { 155 | id: 20, 156 | name: "Elixir (1.5.1)", 157 | is_archived: true, 158 | source_file: "main.exs", 159 | run_cmd: "/usr/local/elixir-1.5.1/bin/elixir main.exs" 160 | }, 161 | { 162 | id: 21, 163 | name: "Erlang (OTP 20.0)", 164 | is_archived: true, 165 | source_file: "main.erl", 166 | run_cmd: "/bin/sed -i \"s/^/\\n/\" main.erl && /usr/local/erlang-20.0/bin/escript main.erl" 167 | }, 168 | { 169 | id: 22, 170 | name: "Go (1.9)", 171 | is_archived: true, 172 | source_file: "main.go", 173 | compile_cmd: "/usr/local/go-1.9/bin/go build %s main.go", 174 | run_cmd: "./main" 175 | }, 176 | { 177 | id: 23, 178 | name: "Haskell (ghc 8.2.1)", 179 | is_archived: true, 180 | source_file: "main.hs", 181 | compile_cmd: "/usr/local/ghc-8.2.1/bin/ghc %s main.hs", 182 | run_cmd: "./main" 183 | }, 184 | { 185 | id: 24, 186 | name: "Haskell (ghc 8.0.2)", 187 | is_archived: true, 188 | source_file: "main.hs", 189 | compile_cmd: "/usr/local/ghc-8.0.2/bin/ghc %s main.hs", 190 | run_cmd: "./main" 191 | }, 192 | { 193 | id: 25, 194 | name: "Insect (5.0.0)", 195 | is_archived: true, 196 | source_file: "main.ins", 197 | run_cmd: "/usr/local/insect-5.0.0/insect main.ins" 198 | }, 199 | { 200 | id: 26, 201 | name: "Java (OpenJDK 9 with Eclipse OpenJ9)", 202 | is_archived: true, 203 | source_file: "Main.java", 204 | compile_cmd: "/usr/local/openjdk9-openj9/bin/javac %s Main.java", 205 | run_cmd: "/usr/local/openjdk9-openj9/bin/java Main" 206 | }, 207 | { 208 | id: 27, 209 | name: "Java (OpenJDK 8)", 210 | is_archived: true, 211 | source_file: "Main.java", 212 | compile_cmd: "/usr/lib/jvm/java-8-openjdk-amd64/bin/javac %s Main.java", 213 | run_cmd: "/usr/lib/jvm/java-8-openjdk-amd64/bin/java Main", 214 | }, 215 | { 216 | id: 28, 217 | name: "Java (OpenJDK 7)", 218 | is_archived: true, 219 | source_file: "Main.java", 220 | compile_cmd: "/usr/lib/jvm/java-7-openjdk-amd64/bin/javac %s Main.java", 221 | run_cmd: "/usr/lib/jvm/java-7-openjdk-amd64/bin/java Main", 222 | }, 223 | { 224 | id: 29, 225 | name: "JavaScript (nodejs 8.5.0)", 226 | is_archived: true, 227 | source_file: "main.js", 228 | run_cmd: "/usr/local/node-8.5.0/bin/node main.js" 229 | }, 230 | { 231 | id: 30, 232 | name: "JavaScript (nodejs 7.10.1)", 233 | is_archived: true, 234 | source_file: "main.js", 235 | run_cmd: "/usr/local/node-7.10.1/bin/node main.js" 236 | }, 237 | { 238 | id: 31, 239 | name: "OCaml (4.05.0)", 240 | is_archived: true, 241 | source_file: "main.ml", 242 | compile_cmd: "/usr/local/ocaml-4.05.0/bin/ocamlc %s main.ml", 243 | run_cmd: "./a.out" 244 | }, 245 | { 246 | id: 32, 247 | name: "Octave (4.2.0)", 248 | is_archived: true, 249 | source_file: "file.m", 250 | run_cmd: "/usr/local/octave-4.2.0/bin/octave-cli -q --no-gui --no-history file.m" 251 | }, 252 | { 253 | id: 33, 254 | name: "Pascal (fpc 3.0.0)", 255 | is_archived: true, 256 | source_file: "main.pas", 257 | compile_cmd: "/usr/local/fpc-3.0.0/bin/fpc %s main.pas", 258 | run_cmd: "./main" 259 | }, 260 | { 261 | id: 34, 262 | name: "Python (3.6.0)", 263 | is_archived: true, 264 | source_file: "main.py", 265 | run_cmd: "/usr/local/python-3.6.0/bin/python3 main.py" 266 | }, 267 | { 268 | id: 35, 269 | name: "Python (3.5.3)", 270 | is_archived: true, 271 | source_file: "main.py", 272 | run_cmd: "/usr/local/python-3.5.3/bin/python3 main.py" 273 | }, 274 | { 275 | id: 36, 276 | name: "Python (2.7.9)", 277 | is_archived: true, 278 | source_file: "main.py", 279 | run_cmd: "/usr/local/python-2.7.9/bin/python main.py" 280 | }, 281 | { 282 | id: 37, 283 | name: "Python (2.6.9)", 284 | is_archived: true, 285 | source_file: "main.py", 286 | run_cmd: "/usr/local/python-2.6.9/bin/python main.py" 287 | }, 288 | { 289 | id: 38, 290 | name: "Ruby (2.4.0)", 291 | is_archived: true, 292 | source_file: "main.rb", 293 | run_cmd: "/usr/local/ruby-2.4.0/bin/ruby main.rb" 294 | }, 295 | { 296 | id: 39, 297 | name: "Ruby (2.3.3)", 298 | is_archived: true, 299 | source_file: "main.rb", 300 | run_cmd: "/usr/local/ruby-2.3.3/bin/ruby main.rb" 301 | }, 302 | { 303 | id: 40, 304 | name: "Ruby (2.2.6)", 305 | is_archived: true, 306 | source_file: "main.rb", 307 | run_cmd: "/usr/local/ruby-2.2.6/bin/ruby main.rb" 308 | }, 309 | { 310 | id: 41, 311 | name: "Ruby (2.1.9)", 312 | is_archived: true, 313 | source_file: "main.rb", 314 | run_cmd: "/usr/local/ruby-2.1.9/bin/ruby main.rb" 315 | }, 316 | { 317 | id: 42, 318 | name: "Rust (1.20.0)", 319 | is_archived: true, 320 | source_file: "main.rs", 321 | compile_cmd: "/usr/local/rust-1.20.0/bin/rustc %s main.rs", 322 | run_cmd: "./main" 323 | } 324 | ] -------------------------------------------------------------------------------- /judge0-api.conf.default: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Rails Environment 3 | ############################################################################### 4 | # Specify Rails environment: production or development 5 | # Default: production 6 | RAILS_ENV= 7 | 8 | # Specify maximum number of concurrent Rails threads. 9 | # Default: 5 10 | RAILS_MAX_THREADS= 11 | 12 | # Maintenance mode is a mode in which clients cannot 13 | # create or delete submissions while maintenance is enabled. 14 | # Default: false 15 | MAINTENANCE_MODE= 16 | 17 | # Set custom maintenance message that will be returned to clients 18 | # who try to create or delete submisions. 19 | # Default: Judge0 API is currently in maintenance. 20 | MAINTENANCE_MESSAGE= 21 | 22 | # Secret key base for production, if not set it will be randomly generated 23 | # Default: randomly generated 24 | SECRET_KEY_BASE= 25 | 26 | # Allow only specified origins. 27 | # If left blank, then all origins will be allowed (denoted with '*'). 28 | # Example: 29 | # ALLOW_ORIGIN="www.judge0.com judge0.com www.example.com blog.example.com" 30 | ALLOW_ORIGIN= 31 | 32 | # Disallow only specified origins. 33 | # If left blank, then no origin will be disallowed. 34 | # Example: 35 | # DISALLOW_ORIGIN="www.judge0.com judge0.com www.example.com blog.example.com" 36 | DISALLOW_ORIGIN= 37 | 38 | # Allow only specified IP addresses. 39 | # If left blank, then all IP addresses will be allowed. 40 | # Example: 41 | # ALLOW_IP="192.168.10.10 96.239.226.228 208.23.207.242" 42 | ALLOW_IP= 43 | 44 | # Disallow only specified IP addresses. 45 | # If left blank, then no IP addresses will be disallowed. 46 | # Example: 47 | # DISALLOW_IP="192.168.10.10 96.239.226.228 208.23.207.242" 48 | DISALLOW_IP= 49 | 50 | 51 | ############################################################################### 52 | # Authentication 53 | ############################################################################### 54 | # You can protect your API with (AUTHN_HEADER, AUTHN_TOKEN) pair. 55 | # Each request then needs to have this pair either in headers or 56 | # query parameters. For example let AUTHN_HEADER=X-Judge0-Token and 57 | # AUTHN_TOKEN=mySecretToken. Then user should authenticate by sending this 58 | # in headers or query parameters in each request, e.g.: 59 | # https://api.judge0.com/system_info?X-Judge0-Token=mySecretToken 60 | 61 | # Specify authentication header name. 62 | # Default: X-Auth-Token 63 | AUTHN_HEADER= 64 | 65 | # Specify valid authentication tokens. 66 | # Default: empty - authentication is disabled 67 | AUTHN_TOKEN= 68 | 69 | 70 | ############################################################################### 71 | # Authorization 72 | ############################################################################### 73 | # Protected API calls can be issued with (AUTHZ_HEADER, AUTHZ_TOKEN) pair. 74 | # To see exactly which API calls are protected with authorization tokens 75 | # please read the docs at https://api.judge0.com. 76 | # API authorization ensures that only specified users call protected API calls. 77 | # For example let AUTHZ_HEADER=X-Judge0-User and AUTHZ_TOKEN=mySecretToken. 78 | # Then user should authorize be sending this in headers or query parameters in 79 | # each request, e.g.: https://api.judge0.com/system_info?X-Judge0-User=mySecretToken 80 | # Note that if you enabled authentication, then user should also send valid 81 | # authentication token. 82 | 83 | # Specify authorization header name. 84 | # Default: X-Auth-User 85 | AUTHZ_HEADER= 86 | 87 | # Specify valid authorization tokens. 88 | # Default: empty - authorization is disabled, protected API calls cannot be issued 89 | AUTHZ_TOKEN= 90 | 91 | 92 | ############################################################################### 93 | # Workers 94 | ############################################################################### 95 | # Specify polling frequency in seconds. Decimal numbers are allowed. 96 | # Default: 0.1 97 | INTERVAL= 98 | 99 | # Specify how many parallel workers to run. 100 | # Default: 1 101 | COUNT= 102 | 103 | # Specify maximum queue size. Represents maximum number of submissions that 104 | # can wait in the queue at once. If request for new submission comes and the 105 | # queue if full then submission will be rejected. 106 | # Default: 100 107 | MAX_QUEUE_SIZE= 108 | 109 | 110 | ############################################################################### 111 | # Database 112 | ############################################################################### 113 | # Specify Redis host 114 | # Default: localhost 115 | REDIS_HOST=redis 116 | 117 | # Specify Redis port. 118 | # Default: 6379 119 | REDIS_PORT= 120 | 121 | # Specify Redis password. Cannot be blank. 122 | # Default: NO DEFAULT! MUST BE SET! 123 | REDIS_PASSWORD=YourPasswordHere1234 124 | 125 | # Specify Postgres host. 126 | # Default: localhost 127 | POSTGRES_HOST=db 128 | 129 | # Specify Postgres port. 130 | # Default: 5432 131 | POSTGRES_PORT= 132 | 133 | # Name of the database to use. Used only in production. 134 | # Default: postgres 135 | POSTGRES_DB= 136 | 137 | # User who can access this database. Used only in production. 138 | # Default: postgres 139 | POSTGRES_USER= 140 | 141 | # Password of the user. Cannot be blank. Used only in production. 142 | # Default: NO DEFAULT, YOU MUST SET YOUR PASSWORD 143 | POSTGRES_PASSWORD=YourPasswordHere1234 144 | 145 | 146 | ############################################################################### 147 | # Configuration 148 | ############################################################################### 149 | # https://raw.githubusercontent.com/ioi/isolate/master/isolate.1.txt 150 | 151 | # If enabled user can request to synchronically wait for submission result on submission create. 152 | # Default: true, i.e. user can request to wait for the result 153 | ENABLE_WAIT_RESULT= 154 | 155 | # If enabled user is allowed to set custom compiler options. 156 | # Default: true 157 | ENABLE_COMPILER_OPTIONS= 158 | 159 | # List language names, separated by space, for which setting compiler options is allowed. 160 | # Note that ENABLE_COMPILER_OPTIONS has higher priority, so this option will be 161 | # ignored if setting compiler options is disabled with ENABLE_COMPILER_OPTIONS. 162 | # For example, ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS="C C++ Java" would only 163 | # allow setting compiler options for languages C, C++ and Java. 164 | # Default: empty - for every compiled language setting compiler options is allowed. 165 | ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS= 166 | 167 | # If enabled user is allowed to set custom command line arguments. 168 | # Default: true 169 | ENABLE_COMMAND_LINE_ARGUMENTS= 170 | 171 | # If enabled autorized users can delete a submission. 172 | # Default: false 173 | ENABLE_SUBMISSION_DELETE= 174 | 175 | # Default runtime limit for every program (in seconds). Decimal numbers are allowed. 176 | # Time in which the OS assigns the processor to different tasks is not counted. 177 | # Default: 2 178 | CPU_TIME_LIMIT= 179 | 180 | # Maximum custom CPU_TIME_LIMIT. 181 | # Default: 15 182 | MAX_CPU_TIME_LIMIT= 183 | 184 | # When a time limit is exceeded, wait for extra time (in seconds), before 185 | # killing the program. This has the advantage that the real execution time 186 | # is reported, even though it slightly exceeds the limit. 187 | # Default: 0.5 188 | CPU_EXTRA_TIME= 189 | 190 | # Maximum custom CPU_EXTRA_TIME. 191 | # Default: 2 192 | MAX_CPU_EXTRA_TIME= 193 | 194 | # Limit wall-clock time in seconds. Decimal numbers are allowed. 195 | # This clock measures the time from the start of the program to its exit, 196 | # so it does not stop when the program has lost the CPU or when it is waiting 197 | # for an external event. We recommend to use CPU_TIME_LIMIT as the main limit, 198 | # but set WALL_TIME_LIMIT to a much higher value as a precaution against 199 | # sleeping programs. 200 | # Default: 5 201 | WALL_TIME_LIMIT= 202 | 203 | # Maximum custom WALL_TIME_LIMIT. 204 | # Default: 20 205 | MAX_WALL_TIME_LIMIT= 206 | 207 | # Limit address space of the program in kilobytes. 208 | # Default: 128000 209 | MEMORY_LIMIT= 210 | 211 | # Maximum custom MEMORY_LIMIT. 212 | # Default: 256000 213 | MAX_MEMORY_LIMIT= 214 | 215 | # Limit process stack in kilobytes. 216 | # Default: 64000 217 | STACK_LIMIT= 218 | 219 | # Maximum custom STACK_LIMIT. 220 | # Default: 128000 221 | MAX_STACK_LIMIT= 222 | 223 | # Maximum number of processes and/or threads program can create. 224 | # Default: 60 225 | MAX_PROCESSES_AND_OR_THREADS= 226 | 227 | # Maximum custom MAX_PROCESSES_AND_OR_THREADS. 228 | # Default: 120 229 | MAX_MAX_PROCESSES_AND_OR_THREADS= 230 | 231 | # If true then CPU_TIME_LIMIT will be used as per process and thread. 232 | # Default: false, i.e. CPU_TIME_LIMIT is set as a total limit for all processes and threads. 233 | ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT= 234 | 235 | # If false, user won't be able to set ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT. 236 | # Default: true 237 | ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT= 238 | 239 | # If true then MEMORY_LIMIT will be used as per process and thread. 240 | # Default: false, i.e. MEMORY_LIMIT is set as a total limit for all processes and threads. 241 | ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT= 242 | 243 | # If false, user won't be able to set ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT. 244 | # Default: true 245 | ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT= 246 | 247 | # Limit size of files created (or modified) by the program in kilobytes. 248 | # Default: 1024 249 | MAX_FILE_SIZE= 250 | 251 | # Maximum custom MAX_FILE_SIZE. 252 | # Default: 4096 253 | MAX_MAX_FILE_SIZE= 254 | 255 | # Run each program this many times and take average of time and memory. 256 | # Default: 1 257 | NUMBER_OF_RUNS= 258 | 259 | # Maximum custom NUMBER_OF_RUNS. 260 | # Default: 20 261 | MAX_NUMBER_OF_RUNS= 262 | 263 | # Redirect stderr to stdout. 264 | # Default: false 265 | REDIRECT_STDERR_TO_STDOUT= 266 | 267 | # Maximum total size (in kilobytes) of extracted files from archive. 268 | # Default: 10240, i.e. maximum of 10MB in total can be extracted. 269 | MAX_EXTRACT_SIZE= 270 | 271 | # Maximum number of submissions that can be created or get in a batch. 272 | # Default: 20 273 | MAX_SUBMISSION_BATCH_SIZE= -------------------------------------------------------------------------------- /app/jobs/isolate_job.rb: -------------------------------------------------------------------------------- 1 | class IsolateJob < ApplicationJob 2 | queue_as ENV["JUDGE0_VERSION"].to_sym 3 | 4 | STDIN_FILE_NAME = "stdin.txt" 5 | STDOUT_FILE_NAME = "stdout.txt" 6 | STDERR_FILE_NAME = "stderr.txt" 7 | METADATA_FILE_NAME = "metadata.txt" 8 | ARCHIVE_FILE_NAME = "archive.zip" 9 | 10 | attr_reader :submission, :cgroups, 11 | :box_id, :workdir, :boxdir, :tmpdir, 12 | :source_file, :stdin_file, :stdout_file, 13 | :stderr_file, :metadata_file, :archive_file 14 | 15 | def perform(submission) 16 | @submission = submission 17 | 18 | time = [] 19 | memory = [] 20 | 21 | submission.update(status: Status.process) 22 | submission.number_of_runs.times do 23 | initialize_workdir 24 | if compile == :failure 25 | cleanup 26 | return 27 | end 28 | run 29 | verify 30 | 31 | time << submission.time 32 | memory << submission.memory 33 | 34 | cleanup 35 | break if submission.status != Status.ac 36 | end 37 | 38 | submission.time = time.inject(&:+).to_f / time.size 39 | submission.memory = memory.inject(&:+).to_f / memory.size 40 | submission.save 41 | 42 | rescue Exception => e 43 | submission.finished_at ||= DateTime.now 44 | submission.update(message: e.message, status: Status.boxerr) 45 | cleanup(raise_exception = false) 46 | ensure 47 | call_callback 48 | end 49 | 50 | private 51 | 52 | def initialize_workdir 53 | @box_id = submission.id%2147483647 54 | @cgroups = (!submission.enable_per_process_and_thread_time_limit || !submission.enable_per_process_and_thread_memory_limit) ? "--cg" : "" 55 | @workdir = `isolate #{cgroups} -b #{box_id} --init`.chomp 56 | @boxdir = workdir + "/box" 57 | @tmpdir = workdir + "/tmp" 58 | @source_file = boxdir + "/" + submission.language.source_file 59 | @stdin_file = workdir + "/" + STDIN_FILE_NAME 60 | @stdout_file = workdir + "/" + STDOUT_FILE_NAME 61 | @stderr_file = workdir + "/" + STDERR_FILE_NAME 62 | @metadata_file = workdir + "/" + METADATA_FILE_NAME 63 | @archive_file = boxdir + "/" + ARCHIVE_FILE_NAME 64 | 65 | [stdin_file, stdout_file, stderr_file, metadata_file].each do |f| 66 | initialize_file(f) 67 | end 68 | 69 | File.open(source_file, "wb") { |f| f.write(submission.source_code) } 70 | File.open(stdin_file, "wb") { |f| f.write(submission.stdin) } 71 | 72 | extract_archive 73 | end 74 | 75 | def initialize_file(file) 76 | `sudo touch #{file} && sudo chown $(whoami): #{file}` 77 | end 78 | 79 | def extract_archive 80 | return unless submission.archive? 81 | 82 | File.open(archive_file, "wb") { |f| f.write(submission.archive) } 83 | 84 | command = "isolate #{cgroups} \ 85 | -s \ 86 | -b #{box_id} \ 87 | -t 2 \ 88 | -x 1 \ 89 | -w 4 \ 90 | -k #{Config::MAX_STACK_LIMIT} \ 91 | -p#{Config::MAX_MAX_PROCESSES_AND_OR_THREADS} \ 92 | #{submission.enable_per_process_and_thread_time_limit ? (cgroups.present? ? "--no-cg-timing" : "") : "--cg-timing"} \ 93 | #{submission.enable_per_process_and_thread_memory_limit ? "-m " : "--cg-mem="}#{Config::MAX_MEMORY_LIMIT} \ 94 | -f #{Config::MAX_EXTRACT_SIZE} \ 95 | --run \ 96 | -- /usr/bin/unzip -n -qq #{ARCHIVE_FILE_NAME} 2>&1 \ 97 | " 98 | 99 | puts "[#{DateTime.now}] Extracting archive for submission #{submission.token} (#{submission.id}):" 100 | puts command.gsub(/\s+/, " ") 101 | puts 102 | 103 | extract_output = `#{command}`.chomp 104 | puts(extract_output) 105 | 106 | File.delete(archive_file) 107 | end 108 | 109 | def compile 110 | return :success unless submission.language.compile_cmd 111 | 112 | # gsub can be skipped if compile script is used, but is kept for additional security. 113 | compiler_options = submission.compiler_options.to_s.strip.encode("UTF-8", invalid: :replace).gsub(/[$&;<>|`]/, "") 114 | 115 | compile_script = boxdir + "/" + "compile" 116 | File.open(compile_script, "w") { |f| f.write("#{submission.language.compile_cmd % compiler_options}")} 117 | 118 | command = "isolate #{cgroups} \ 119 | -s \ 120 | -b #{box_id} \ 121 | -M #{metadata_file} \ 122 | -t 5 \ 123 | -x 2 \ 124 | -w 10 \ 125 | -k #{Config::MAX_STACK_LIMIT} \ 126 | -p#{Config::MAX_MAX_PROCESSES_AND_OR_THREADS} \ 127 | #{submission.enable_per_process_and_thread_time_limit ? (cgroups.present? ? "--no-cg-timing" : "") : "--cg-timing"} \ 128 | #{submission.enable_per_process_and_thread_memory_limit ? "-m " : "--cg-mem="}#{Config::MAX_MEMORY_LIMIT} \ 129 | -f #{Config::MAX_MAX_FILE_SIZE} \ 130 | -E HOME=#{workdir} \ 131 | -E PATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\" \ 132 | -E LANG -E LANGUAGE -E LC_ALL -E JUDGE0_HOMEPAGE -E JUDGE0_SOURCE_CODE -E JUDGE0_MAINTAINER -E JUDGE0_VERSION \ 133 | -d /etc:noexec \ 134 | --run \ 135 | -- /bin/bash compile 2>&1 \ 136 | " 137 | 138 | puts "[#{DateTime.now}] Compiling submission #{submission.token} (#{submission.id}):" 139 | puts command.gsub(/\s+/, " ") 140 | puts 141 | 142 | compile_output = `#{command}`.chomp 143 | process_status = $? 144 | 145 | compile_output = nil if compile_output.empty? 146 | submission.compile_output = compile_output 147 | 148 | metadata = get_metadata 149 | 150 | reset_metadata_file 151 | `sudo chown $(whoami): #{compile_script} && rm #{compile_script}` 152 | 153 | return :success if process_status.success? 154 | 155 | if metadata[:status] == "TO" 156 | submission.compile_output = "Compilation time limit exceeded." 157 | end 158 | 159 | submission.finished_at ||= DateTime.now 160 | submission.time = nil 161 | submission.wall_time = nil 162 | submission.memory = nil 163 | submission.stdout = nil 164 | submission.stderr = nil 165 | submission.exit_code = nil 166 | submission.exit_signal = nil 167 | submission.message = nil 168 | submission.status = Status.ce 169 | submission.save 170 | 171 | return :failure 172 | end 173 | 174 | def run 175 | # gsub is mandatory! 176 | command_line_arguments = submission.command_line_arguments.to_s.strip.encode("UTF-8", invalid: :replace).gsub(/[$&;<>|`]/, "") 177 | 178 | run_script = boxdir + "/" + "run" 179 | File.open(run_script, "w") { |f| f.write("#{submission.language.run_cmd} #{command_line_arguments}")} 180 | 181 | command = "isolate #{cgroups} \ 182 | -s \ 183 | -b #{box_id} \ 184 | -M #{metadata_file} \ 185 | #{submission.redirect_stderr_to_stdout ? "--stderr-to-stdout" : ""} \ 186 | -t #{submission.cpu_time_limit} \ 187 | -x #{submission.cpu_extra_time} \ 188 | -w #{submission.wall_time_limit} \ 189 | -k #{submission.stack_limit} \ 190 | -p#{submission.max_processes_and_or_threads} \ 191 | #{submission.enable_per_process_and_thread_time_limit ? (cgroups.present? ? "--no-cg-timing" : "") : "--cg-timing"} \ 192 | #{submission.enable_per_process_and_thread_memory_limit ? "-m " : "--cg-mem="}#{submission.memory_limit} \ 193 | -f #{submission.max_file_size} \ 194 | -E HOME=#{workdir} \ 195 | -E PATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\" \ 196 | -E LANG -E LANGUAGE -E LC_ALL -E JUDGE0_HOMEPAGE -E JUDGE0_SOURCE_CODE -E JUDGE0_MAINTAINER -E JUDGE0_VERSION \ 197 | -d /etc:noexec \ 198 | --run \ 199 | -- /bin/bash run \ 200 | < #{stdin_file} > #{stdout_file} 2> #{stderr_file} \ 201 | " 202 | 203 | puts "[#{DateTime.now}] Running submission #{submission.token} (#{submission.id}):" 204 | puts command.gsub(/\s+/, " ") 205 | puts 206 | 207 | `#{command}` 208 | 209 | `sudo chown $(whoami): #{run_script} && rm #{run_script}` 210 | end 211 | 212 | def verify 213 | submission.finished_at ||= DateTime.now 214 | 215 | metadata = get_metadata 216 | 217 | program_stdout = File.read(stdout_file) 218 | program_stdout = nil if program_stdout.empty? 219 | 220 | program_stderr = File.read(stderr_file) 221 | program_stderr = nil if program_stderr.empty? 222 | 223 | submission.time = metadata[:time] 224 | submission.wall_time = metadata[:"time-wall"] 225 | submission.memory = (cgroups.present? ? metadata[:"cg-mem"] : metadata[:"max-rss"]) 226 | submission.stdout = program_stdout 227 | submission.stderr = program_stderr 228 | submission.exit_code = metadata[:exitcode].try(:to_i) || 0 229 | submission.exit_signal = metadata[:exitsig].try(:to_i) 230 | submission.message = metadata[:message] 231 | submission.status = determine_status(metadata[:status], submission.exit_signal) 232 | 233 | # After adding support for compiler_options and command_line_arguments 234 | # status "Exec Format Error" will no longer occur because compile and run 235 | # is done inside a dynamically created bash script, thus isolate doesn't call 236 | # execve directily on submission.language.compile_cmd or submission.langauge.run_cmd. 237 | # Consequence of running compile and run through bash script is that when 238 | # target binary is not found then submission gets status "Runtime Error (NZEC)". 239 | # 240 | # I think this is for now O.K. behaviour, but I will leave this if block 241 | # here until I am 100% sure that "Exec Format Error" can be deprecated. 242 | if submission.status == Status.boxerr && 243 | ( 244 | submission.message.to_s.match(/^execve\(.+\): Exec format error$/) || 245 | submission.message.to_s.match(/^execve\(.+\): No such file or directory$/) || 246 | submission.message.to_s.match(/^execve\(.+\): Permission denied$/) 247 | ) 248 | submission.status = Status.exeerr 249 | end 250 | end 251 | 252 | def cleanup(raise_exception = true) 253 | fix_permissions 254 | `sudo rm -rf #{boxdir}/* #{tmpdir}/*` 255 | `isolate #{cgroups} -b #{box_id} --cleanup` 256 | raise "Cleanup of sandbox #{box_id} failed." if raise_exception && Dir.exists?(workdir) 257 | end 258 | 259 | def reset_metadata_file 260 | `sudo rm -rf #{metadata_file}` 261 | initialize_file(metadata_file) 262 | end 263 | 264 | def fix_permissions 265 | `sudo chown -R $(whoami): #{boxdir}` 266 | end 267 | 268 | def call_callback 269 | return unless submission.callback_url.present? 270 | 271 | serialized_submission = ActiveModelSerializers::SerializableResource.new( 272 | submission, 273 | { 274 | serializer: SubmissionSerializer, 275 | base64_encoded: true, 276 | fields: SubmissionSerializer.default_fields 277 | } 278 | ) 279 | 280 | response = HTTParty.put( 281 | submission.callback_url, 282 | body: serialized_submission.to_json, 283 | headers: { 284 | "Content-Type" => "application/json" 285 | }, 286 | timeout: 2 287 | ) 288 | rescue Exception => e 289 | end 290 | 291 | def get_metadata 292 | metadata = File.read(metadata_file).split("\n").collect do |e| 293 | { e.split(":").first.to_sym => e.split(":")[1..-1].join(":") } 294 | end.reduce({}, :merge) 295 | return metadata 296 | end 297 | 298 | def determine_status(status, exit_signal) 299 | if status == "TO" 300 | return Status.tle 301 | elsif status == "SG" 302 | return Status.find_runtime_error_by_status_code(exit_signal) 303 | elsif status == "RE" 304 | return Status.nzec 305 | elsif status == "XX" 306 | return Status.boxerr 307 | elsif submission.expected_output.nil? || strip(submission.expected_output) == strip(submission.stdout) 308 | return Status.ac 309 | else 310 | return Status.wa 311 | end 312 | end 313 | 314 | def strip(text) 315 | return nil unless text 316 | text.split("\n").collect(&:rstrip).join("\n").rstrip 317 | rescue ArgumentError 318 | return text 319 | end 320 | end 321 | -------------------------------------------------------------------------------- /docs/api/statuses_and_languages/get_active_and_archived_languages.md: -------------------------------------------------------------------------------- 1 | ## Active and Archived Languages [/languages/all] 2 | ### Get Active and Archived Languages [GET] 3 | Get active and archived languages. 4 | 5 | + Response 200 (application/json) 6 | + Body 7 | [ 8 | { 9 | "id": 45, 10 | "name": "Assembly (NASM 2.14.02)", 11 | "is_archived": false 12 | }, 13 | { 14 | "id": 2, 15 | "name": "Bash (4.0)", 16 | "is_archived": true 17 | }, 18 | { 19 | "id": 1, 20 | "name": "Bash (4.4)", 21 | "is_archived": true 22 | }, 23 | { 24 | "id": 46, 25 | "name": "Bash (5.0.0)", 26 | "is_archived": false 27 | }, 28 | { 29 | "id": 3, 30 | "name": "Basic (fbc 1.05.0)", 31 | "is_archived": true 32 | }, 33 | { 34 | "id": 47, 35 | "name": "Basic (FBC 1.07.1)", 36 | "is_archived": false 37 | }, 38 | { 39 | "id": 15, 40 | "name": "C++ (g++ 4.8.5)", 41 | "is_archived": true 42 | }, 43 | { 44 | "id": 14, 45 | "name": "C++ (g++ 4.9.4)", 46 | "is_archived": true 47 | }, 48 | { 49 | "id": 13, 50 | "name": "C++ (g++ 5.4.0)", 51 | "is_archived": true 52 | }, 53 | { 54 | "id": 12, 55 | "name": "C++ (g++ 6.3.0)", 56 | "is_archived": true 57 | }, 58 | { 59 | "id": 11, 60 | "name": "C++ (g++ 6.4.0)", 61 | "is_archived": true 62 | }, 63 | { 64 | "id": 10, 65 | "name": "C++ (g++ 7.2.0)", 66 | "is_archived": true 67 | }, 68 | { 69 | "id": 9, 70 | "name": "C (gcc 4.8.5)", 71 | "is_archived": true 72 | }, 73 | { 74 | "id": 8, 75 | "name": "C (gcc 4.9.4)", 76 | "is_archived": true 77 | }, 78 | { 79 | "id": 7, 80 | "name": "C (gcc 5.4.0)", 81 | "is_archived": true 82 | }, 83 | { 84 | "id": 6, 85 | "name": "C (gcc 6.3.0)", 86 | "is_archived": true 87 | }, 88 | { 89 | "id": 5, 90 | "name": "C (gcc 6.4.0)", 91 | "is_archived": true 92 | }, 93 | { 94 | "id": 4, 95 | "name": "C (gcc 7.2.0)", 96 | "is_archived": true 97 | }, 98 | { 99 | "id": 48, 100 | "name": "C (GCC 7.4.0)", 101 | "is_archived": false 102 | }, 103 | { 104 | "id": 52, 105 | "name": "C++ (GCC 7.4.0)", 106 | "is_archived": false 107 | }, 108 | { 109 | "id": 49, 110 | "name": "C (GCC 8.3.0)", 111 | "is_archived": false 112 | }, 113 | { 114 | "id": 53, 115 | "name": "C++ (GCC 8.3.0)", 116 | "is_archived": false 117 | }, 118 | { 119 | "id": 50, 120 | "name": "C (GCC 9.2.0)", 121 | "is_archived": false 122 | }, 123 | { 124 | "id": 54, 125 | "name": "C++ (GCC 9.2.0)", 126 | "is_archived": false 127 | }, 128 | { 129 | "id": 18, 130 | "name": "Clojure (1.8.0)", 131 | "is_archived": true 132 | }, 133 | { 134 | "id": 17, 135 | "name": "C# (mono 5.2.0.224)", 136 | "is_archived": true 137 | }, 138 | { 139 | "id": 16, 140 | "name": "C# (mono 5.4.0.167)", 141 | "is_archived": true 142 | }, 143 | { 144 | "id": 51, 145 | "name": "C# (Mono 6.6.0.161)", 146 | "is_archived": false 147 | }, 148 | { 149 | "id": 55, 150 | "name": "Common Lisp (SBCL 2.0.0)", 151 | "is_archived": false 152 | }, 153 | { 154 | "id": 19, 155 | "name": "Crystal (0.23.1)", 156 | "is_archived": true 157 | }, 158 | { 159 | "id": 56, 160 | "name": "D (DMD 2.089.1)", 161 | "is_archived": false 162 | }, 163 | { 164 | "id": 20, 165 | "name": "Elixir (1.5.1)", 166 | "is_archived": true 167 | }, 168 | { 169 | "id": 57, 170 | "name": "Elixir (1.9.4)", 171 | "is_archived": false 172 | }, 173 | { 174 | "id": 21, 175 | "name": "Erlang (OTP 20.0)", 176 | "is_archived": true 177 | }, 178 | { 179 | "id": 58, 180 | "name": "Erlang (OTP 22.2)", 181 | "is_archived": false 182 | }, 183 | { 184 | "id": 44, 185 | "name": "Executable", 186 | "is_archived": false 187 | }, 188 | { 189 | "id": 59, 190 | "name": "Fortran (GFortran 9.2.0)", 191 | "is_archived": false 192 | }, 193 | { 194 | "id": 60, 195 | "name": "Go (1.13.5)", 196 | "is_archived": false 197 | }, 198 | { 199 | "id": 22, 200 | "name": "Go (1.9)", 201 | "is_archived": true 202 | }, 203 | { 204 | "id": 24, 205 | "name": "Haskell (ghc 8.0.2)", 206 | "is_archived": true 207 | }, 208 | { 209 | "id": 23, 210 | "name": "Haskell (ghc 8.2.1)", 211 | "is_archived": true 212 | }, 213 | { 214 | "id": 61, 215 | "name": "Haskell (GHC 8.8.1)", 216 | "is_archived": false 217 | }, 218 | { 219 | "id": 25, 220 | "name": "Insect (5.0.0)", 221 | "is_archived": true 222 | }, 223 | { 224 | "id": 62, 225 | "name": "Java (OpenJDK 13.0.1)", 226 | "is_archived": false 227 | }, 228 | { 229 | "id": 28, 230 | "name": "Java (OpenJDK 7)", 231 | "is_archived": true 232 | }, 233 | { 234 | "id": 27, 235 | "name": "Java (OpenJDK 8)", 236 | "is_archived": true 237 | }, 238 | { 239 | "id": 26, 240 | "name": "Java (OpenJDK 9 with Eclipse OpenJ9)", 241 | "is_archived": true 242 | }, 243 | { 244 | "id": 63, 245 | "name": "JavaScript (Node.js 12.14.0)", 246 | "is_archived": false 247 | }, 248 | { 249 | "id": 30, 250 | "name": "JavaScript (nodejs 7.10.1)", 251 | "is_archived": true 252 | }, 253 | { 254 | "id": 29, 255 | "name": "JavaScript (nodejs 8.5.0)", 256 | "is_archived": true 257 | }, 258 | { 259 | "id": 64, 260 | "name": "Lua (5.3.5)", 261 | "is_archived": false 262 | }, 263 | { 264 | "id": 31, 265 | "name": "OCaml (4.05.0)", 266 | "is_archived": true 267 | }, 268 | { 269 | "id": 65, 270 | "name": "OCaml (4.09.0)", 271 | "is_archived": false 272 | }, 273 | { 274 | "id": 32, 275 | "name": "Octave (4.2.0)", 276 | "is_archived": true 277 | }, 278 | { 279 | "id": 66, 280 | "name": "Octave (5.1.0)", 281 | "is_archived": false 282 | }, 283 | { 284 | "id": 33, 285 | "name": "Pascal (fpc 3.0.0)", 286 | "is_archived": true 287 | }, 288 | { 289 | "id": 67, 290 | "name": "Pascal (FPC 3.0.4)", 291 | "is_archived": false 292 | }, 293 | { 294 | "id": 68, 295 | "name": "PHP (7.4.1)", 296 | "is_archived": false 297 | }, 298 | { 299 | "id": 43, 300 | "name": "Plain Text", 301 | "is_archived": false 302 | }, 303 | { 304 | "id": 69, 305 | "name": "Prolog (GNU Prolog 1.4.5)", 306 | "is_archived": false 307 | }, 308 | { 309 | "id": 37, 310 | "name": "Python (2.6.9)", 311 | "is_archived": true 312 | }, 313 | { 314 | "id": 70, 315 | "name": "Python (2.7.17)", 316 | "is_archived": false 317 | }, 318 | { 319 | "id": 36, 320 | "name": "Python (2.7.9)", 321 | "is_archived": true 322 | }, 323 | { 324 | "id": 35, 325 | "name": "Python (3.5.3)", 326 | "is_archived": true 327 | }, 328 | { 329 | "id": 34, 330 | "name": "Python (3.6.0)", 331 | "is_archived": true 332 | }, 333 | { 334 | "id": 71, 335 | "name": "Python (3.8.1)", 336 | "is_archived": false 337 | }, 338 | { 339 | "id": 41, 340 | "name": "Ruby (2.1.9)", 341 | "is_archived": true 342 | }, 343 | { 344 | "id": 40, 345 | "name": "Ruby (2.2.6)", 346 | "is_archived": true 347 | }, 348 | { 349 | "id": 39, 350 | "name": "Ruby (2.3.3)", 351 | "is_archived": true 352 | }, 353 | { 354 | "id": 38, 355 | "name": "Ruby (2.4.0)", 356 | "is_archived": true 357 | }, 358 | { 359 | "id": 72, 360 | "name": "Ruby (2.7.0)", 361 | "is_archived": false 362 | }, 363 | { 364 | "id": 42, 365 | "name": "Rust (1.20.0)", 366 | "is_archived": true 367 | }, 368 | { 369 | "id": 73, 370 | "name": "Rust (1.40.0)", 371 | "is_archived": false 372 | }, 373 | { 374 | "id": 74, 375 | "name": "TypeScript (3.7.4)", 376 | "is_archived": false 377 | } 378 | ] -------------------------------------------------------------------------------- /public/dummy-client.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Judge0 API Dummy Client 6 | 7 | 8 | 9 | 10 |

Judge0 API Dummy Client

11 |

12 | Judge0 API dummy client can be used to try and test features of Judge0 API. Data is not validated on client side. 13 | Client first creates new submission and then fetches submission status every 1500ms. 14 | Client stops fetching submission status after recieving an error or when submission status is not In Queue or Processing. 15 |

16 |

17 | Note that you need to define API URL. This is the base url of some Judge0 API host (e.g. https://api.judge0.com). 18 | Description of network errors can be found in console of your browser, so check that for more information. 19 |

20 | 21 |
22 | 23 | API URL  24 |

25 | AUTHENTICATION HEADER  26 |

27 | AUTHENTICATION TOKEN  28 |

29 | AUTHORIZATION HEADER  30 |

31 | AUTHORIZATION TOKEN  32 |

33 | Source Code 34 | null
35 |

44 | Language ID  45 | 46 | null

47 | Number Of Runs  48 | 49 | null

50 | 51 | CPU Time Limit  52 | 53 | null

54 | 55 | CPU Extra Time  56 | 57 | null

58 | 59 | Wall Time Limit  60 | 61 | null

62 | 63 | Memory Limit  64 | 65 | null

66 | 67 | Stack Limit  68 | 69 | null

70 | 71 | Max Processes And Or Threads  72 | 73 | null

74 | 75 | Enable Per Process And Thread Time Limit 76 | true 77 | false 78 | null

79 | 80 | Enable Per Process And Thread Memory Limit 81 | true 82 | false 83 | null

84 | 85 | Max File Size  86 | 87 | null

88 | 89 | Stdin 90 | null
91 |

92 | 93 | Expected Output 94 | null
95 |

96 | 97 | Fields  98 |

99 | 100 | 101 | Wait for submission

102 | 103 | 104 | Send request with Base64 encoded data

105 | 106 | 107 | Accept response with Base64 encoded data

108 | 109 |
110 | 111 | 112 | 113 | 114 |
115 | 116 |
117 | 118 |

Request/Response Log


119 |

120 | 
121 |   
333 | 
334 | 
335 | 


--------------------------------------------------------------------------------