import Users from require "models"
bind = require "utils.binding"
-bind\bind 'test', -> 'Test called!!!'
-
bind\bind_static 'executer', require 'facades.executer'
bind\bind_static 'crypto', -> require 'services.crypto'
@before_filter =>
@navbar = {}
- @navbar.selected = 1
+ @navbar.selected = -1
['index': "/"]: require "controllers.index"
+
['account.login': "/login"]: require "controllers.account.login"
['account.logout': "/logout"]: require "controllers.account.logout"
['account.register': "/register"]: require "controllers.account.register"
['executer.status_update': "/executer/status_update"]: require "controllers.executer.status_update"
['executer.request': '/executer/request']: require "controllers.executer.request"
- [test: "/test"]: =>
- "Your value is over 10"
+ ['admin.user': "/admin/user"]: => "NOT IMPLEMENTED"
+
+ ['admin.problem': "/admin/problem"]: require "controllers.admin.problem"
+ ['admin.problem.new': "/admin/problem/new"]: require "controllers.admin.problem.new"
+ ['admin.problem.edit': "/admin/problem/edit/:problem_name"]: require "controllers.admin.problem.edit"
+
+ ['admin.submission': "/admin/submission"]: => "NOT IMPLEMENTED"
+ ['admin.competition': "/admin/competition"]: => "NOT IMPLEMENTED"
"/console": console.make!
import make_controller from require "controllers.controller"
import Users from require 'models'
import assert_valid from require "lapis.validate"
+import capture_errors, yield_error from require "lapis.application"
utils = require "lapis.util"
@flow 'csrf_setup'
render: 'account.login'
- post: =>
+ post: capture_errors =>
@flow 'csrf_validate'
assert_valid @params, {
{ "password", exists: true, min_length: 2 }
}
- users = Users\find username: @params.username
- if #users > 0
- if @crypto.verify @params.password, users[1].password_hash
- @session.user_id = users[1].id
+ user = Users\find username: @params.username
+ if user != nil
+ if @crypto.verify @params.password, user.password_hash
+ @session.user_id = user.id
return redirect_to: @url_for 'index'
+ else
+ yield_error "Password incorrect"
+ else
+ yield_error "User not found"
render: 'account.login'
import make_controller from require "controllers.controller"
import Users from require 'models'
import assert_valid from require "lapis.validate"
-import capture_errors from require 'lapis.application'
+import capture_errors, yield_error from require 'lapis.application'
make_controller
inject:
users = Users\find username: @params.username
if users
-- Account already exists
- @errors or= {}
- table.insert @errors, 'Username already taken'
+ yield_error 'Username already taken'
return render: 'account.register'
user_id = Users\create {
--- /dev/null
+include_rules
--- /dev/null
+import make_controller from require "controllers.controller"
+import assert_valid from require "lapis.validate"
+import capture_errors from require 'lapis.application'
+import Problems from require 'models'
+
+make_controller
+ layout: require 'views.partials.admin_layout'
+
+ middleware: { 'logged_in', 'admin_required' }
+
+ get: =>
+ @navbar.selected = 1
+
+ @problems = Problems\select!
+
+ render: 'admin.problem'
+
+
--- /dev/null
+include_rules
--- /dev/null
+import make_controller from require "controllers.controller"
+import assert_valid from require "lapis.validate"
+import capture_errors, capture_errors_json, yield_error from require 'lapis.application'
+import Problems from require 'models'
+
+make_controller
+ layout: require 'views.partials.admin_layout'
+
+ middleware: { 'logged_in', 'admin_required' }
+
+ get: capture_errors_json =>
+ @flow 'csrf_setup'
+
+ @navbar.selected = 1
+
+ assert_valid @params, {
+ { "problem_name", exists: true }
+ }
+
+ @problem = Problems\find short_name: @params.problem_name
+ unless @problem
+ yield_error "Problem not found"
+
+ render: true
+
+ post: capture_errors =>
+ @flow 'csrf_validate'
+ @navbar.selected = 1
+
+ assert_valid @params, {
+ { "problem_name", exists: true }
+ { "problem_id", exists: true }
+ { "name", exists: true }
+ { "short_name", exists: true }
+ { "description", exists: true }
+ { "time_limit", exists: true, is_integer: true }
+ }
+
+ problem = Problems\find @params.problem_id
+ unless problem
+ yield_error "Problem with id '#{@params.problem_id}' not found"
+ return
+
+ problem\update {
+ name: @params.name
+ short_name: @params.short_name
+ description: @params.description
+ time_limit: @params.time_limit
+ }
+
+ redirect_to: (@url_for "admin.problem.edit", problem_name: @params.short_name)
+
+ delete: capture_errors_json =>
+ return json: @params
--- /dev/null
+import make_controller from require "controllers.controller"
+import assert_valid from require "lapis.validate"
+import capture_errors, capture_errors_json, yield_error from require 'lapis.application'
+import Problems from require 'models'
+
+make_controller
+ layout: require 'views.partials.admin_layout'
+
+ middleware: { 'logged_in', 'admin_required' }
+
+ get: =>
+ @flow 'csrf_setup'
+
+ @navbar.selected = 1
+
+ @problems = Problems\select!
+
+ return {
+ layout: require 'views.partials.admin_layout'
+ render: 'admin.problem.new'
+ }
+
+ post: capture_errors =>
+ @flow 'csrf_validate'
+
+ assert_valid @params, {
+ { 'short_name', exists: true }
+ { 'name', exists: true }
+ { 'description', exists: true }
+ { 'time_limit', exists: true, is_integer: true }
+ }
+
+ if Problems\find short_name: @params.short_name
+ yield_error "Problem with short name, #{@params.short_name}, already exists"
+ return
+
+ problem = Problems\create {
+ short_name: @params.short_name
+ name: @params.name
+ kind: Problems.kinds\for_db 'code'
+ description: @params.description
+ time_limit: @params.time_limit
+ }
+
+ if problem then
+ yield_error success: true, msg: "Successfully created problem #{@params.name}"
+ else
+ yield_error "There was an error creating the problem"
for middleware in *routes.middleware
require("#{@app.middleware_prefix}.#{middleware}") @
- GET: => routes.get(@)
- POST: => routes.post(@)
+ GET: =>
+ r = routes.get(@)
+ r.layout or= routes.layout
+ return r
+ POST: =>
+ r = routes.post(@)
+ r.layout or= routes.layout
+ return r
+ DELETE: =>
+ r = routes.delete(@)
+ r.layout or= routes.layout
+ return r
}
}
--- /dev/null
+=>
+ unless @user.username == "admin"
+ @write redirect_to: @url_for 'index'
+
import create_table, add_column, types from require "lapis.db.schema"
+import insert from require "lapis.db"
{
[1]: =>
[3]: =>
create_table "problems", {
{ "id", types.serial },
+ { "short_name", types.varchar unique: true },
{ "name", types.varchar },
{ "kind", types.enum },
{ "description", types.text null: true },
"PRIMARY KEY (id)"
}
+
+ [5]: =>
+ insert "users", {
+ username: "admin"
+ password_hash: "$2b$10$uZasTmdngnbGO4ogkvY9b.S7bn.YxLJseCc3MufyX7S0wr5UpgNxy"
+ nickname: "admin"
+ email: "admin@admin.org"
+ }
}
bcrypt = require "bcrypt"
-import Users from require "models"
-
ROUNDS = 10
{
color: white;
}
+.content, .error-list {
+ width: 100%;
+ padding: 10px 10%;
+}
+
+.right {
+ float: right;
+}
+
+.left {
+ float: left;
+}
+
+.hidden {
+ visibility: hidden;
+}
+
+a {
+ text-decoration: none;
+ color: inherit;
+}
+
.sidebar-problem-list {
z-index: 50;
box-shadow: 0 10px 0 0 transparent, 0 10px 0 0 transparent, 10px 0 0 0 transparent, 0 8px 8px 2px rgba(0, 0, 0, 0.5);
font-size: 16px;
}
+h1 {
+ width: 100%;
+ margin-top: 12px;
+ text-align: center;
+}
+
body {
font-family: sans-serif;
background: url(/static/imgs/grain_background.png);
color: white;
}
+button {
+ box-shadow: 0px 2px 6px 0.5px rgba(0, 0, 0, 0.7);
+ border: 1px solid #0077c2;
+ background-color: #0077c2;
+ color: white;
+ font-size: 1rem;
+ padding: 0.75rem;
+ margin-bottom: 12px;
+ display: inline-block;
+}
+button::placeholder {
+ color: #999;
+}
+
+form input, form button, form textarea {
+ box-shadow: 0px 2px 6px 0.5px rgba(0, 0, 0, 0.7);
+ border: 1px solid #0077c2;
+ background-color: #0077c2;
+ color: white;
+ font-size: 1rem;
+ padding: 0.75rem;
+ margin-bottom: 12px;
+ display: inline-block;
+ width: 100%;
+}
+form input::placeholder, form button::placeholder, form textarea::placeholder {
+ color: #999;
+}
+form textarea {
+ background-color: #111;
+ height: 50vh;
+}
+form label {
+ display: inline-block;
+ padding-left: 12px;
+ margin-top: 16px;
+ margin-bottom: 8px;
+}
+
+.error-list ul li {
+ box-shadow: 0px 2px 6px 0.5px rgba(0, 0, 0, 0.5);
+ list-style: none;
+ background-color: #998100;
+ padding: 0.5rem;
+ margin-bottom: 0.5rem;
+}
+.error-list ul li.success {
+ background-color: #0077c2;
+}
+
/*# sourceMappingURL=core.css.map */
-{"version":3,"sourceRoot":"","sources":["../scss/_navbar.scss","../scss/_vars.scss","../scss/problem/_sidebar.scss","../scss/core.scss"],"names":[],"mappings":"AAAA;EACC;EACA;EACA,YCIc;EDHd,QCgBe;EDff;EAEA;EAEA;EACA;;AAEA;EACC;EACA;;AAEA;EACC;EACA;;AAIF;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA,kBClCY;EDoCZ;;AAEA;EACC;EACA;;;AE/CJ;EACC;EACA;EACA;EACA;EACA;;AAEA;EACC;EAEA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;AAIF;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;;AC9CF;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EAEA;EACA;EACA;EACA","file":"core.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["../scss/_navbar.scss","../scss/_vars.scss","../scss/_utils.scss","../scss/problem/_sidebar.scss","../scss/core.scss","../scss/_elevate.scss"],"names":[],"mappings":"AAAA;EACC;EACA;EACA,YCIc;EDHd,QCgBe;EDff;EAEA;EAEA;EACA;;AAEA;EACC;EACA;;AAEA;EACC;EACA;;AAIF;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA,kBClCY;EDoCZ;;AAEA;EACC;EACA;;;AE/CJ;EACC;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;;ACnBD;EACC;EACA;EACA;EACA;EACA;;AAEA;EACC;EAEA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;AAIF;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;;AC7CF;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;;;AAGD;EC/BI;EDiCH;EACA,kBH5Bc;EG6Bd;EAEA;EACA;EACA;EACA;;AAEA;EACC;;;AAKD;EChDG;EDkDF;EACA,kBH7Ca;EG8Cb;EAEA;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAIF;EACC;EACA;;AAGD;EACC;EACA;EACA;EACA;;;AAOD;ECjFG;EDoFF;EACA,kBH3Ee;EG4Ef;EACA;;AAEA;EACC,kBHpFY","file":"core.css"}
\ No newline at end of file
-.content {
+%content, .content {
width: 100%;
- padding: 10px 5%;
+ padding: 10px 10%;
}
.right {
@import '_vars';
@import '_elevate';
@import '_navbar';
+@import '_utils';
@import 'problem/_list.scss';
@import 'problem/_sidebar.scss';
font-size: 16px;
}
+h1 {
+ width: 100%;
+ margin-top: 12px;
+ text-align: center;
+}
+
body {
font-family: sans-serif;
background-repeat: repeat;
color: white;
}
+
+button {
+ @include elevate(2px, rgba(0, 0, 0, 0.7));
+ border: 1px solid $primary-dark;
+ background-color: $primary-dark;
+ color: white;
+
+ font-size: 1rem;
+ padding: .75rem;
+ margin-bottom: 12px;
+ display: inline-block;
+
+ &::placeholder {
+ color: #999;
+ }
+}
+
+form {
+ input, button, textarea {
+ @include elevate(2px, rgba(0, 0, 0, 0.7));
+ border: 1px solid $primary-dark;
+ background-color: $primary-dark;
+ color: white;
+
+ font-size: 1rem;
+ padding: .75rem;
+ margin-bottom: 12px;
+ display: inline-block;
+ width: 100%;
+
+ &::placeholder {
+ color: #999;
+ }
+ }
+
+ textarea {
+ background-color: #111;
+ height: 50vh;
+ }
+
+ label {
+ display: inline-block;
+ padding-left: 12px;
+ margin-top: 16px;
+ margin-bottom: 8px;
+ }
+}
+
+.error-list {
+ @extend .content;
+
+ ul li {
+ @include elevate(2px, rgba(0, 0, 0, 0.5));
+
+ list-style: none;
+ background-color: $secondary-dark;
+ padding: .5rem;
+ margin-bottom: .5rem;
+
+ &.success {
+ background-color: $primary-dark;
+ }
+ }
+}
html = require "lapis.html"
+import to_json from require 'lapis.util'
class Login extends html.Widget
content: =>
- h1 style: "background-color:red", "Login!"
+ h1 "Login"
- form method: 'POST', ->
- input type: 'hidden', name: 'csrf_token', value: @csrf_token
- input type: 'text', placeholder: 'Username', name: 'username', ''
- input type: 'password', placeholder: 'Password', name: 'password', ''
- input type: 'submit', value: 'Submit', ''
+ div class: 'content', ->
+ form method: 'POST', ->
+ input type: 'hidden', name: 'csrf_token', value: @csrf_token
+ input type: 'text', placeholder: 'Username', name: 'username', ''
+ input type: 'password', placeholder: 'Password', name: 'password', ''
+ input type: 'submit', value: 'Submit', ''
class Register extends html.Widget
content: =>
- h1 style: "background-color:red", "Register"
+ h1 "Register"
- p ->
- text (to_json @errors)
+ div class: 'content', ->
+ form method: 'POST', ->
+ input type: 'hidden', name: 'csrf_token', value: @csrf_token
+
+ label for: 'username', 'Username'
+ p -> input type: 'text', placeholder: 'Username', name: 'username', required: true, ''
- form method: 'POST', ->
- p -> input type: 'hidden', name: 'csrf_token', value: @csrf_token
- p -> input type: 'text', placeholder: 'Username', name: 'username', required: true, ''
- p -> input type: 'password', placeholder: 'Password', name: 'password', required: true, ''
- p -> input type: 'password', placeholder: 'Confirm Password', name: 'password_confirmation', required: true, ''
- p -> input type: 'text', placeholder: 'Email', name: 'email', required: true, ''
- p -> input type: 'text', placeholder: 'Display Name', name: 'nickname', required: true, ''
- input type: 'submit', value: 'Submit', ''
+ label for: 'password', 'Password'
+ p -> input type: 'password', placeholder: 'Password', name: 'password', required: true, ''
+
+ label for: 'password_confirmation', 'Confirm Password'
+ p -> input type: 'password', placeholder: 'Confirm Password', name: 'password_confirmation', required: true, ''
+
+ label for: 'email', 'Email'
+ p -> input type: 'text', placeholder: 'Email', name: 'email', required: true, ''
+
+ label for: 'nickname', 'Display name'
+ p -> input type: 'text', placeholder: 'Display Name', name: 'nickname', required: true, ''
+
+ input type: 'submit', value: 'Submit', ''
--- /dev/null
+include_rules
--- /dev/null
+html = require "lapis.html"
+
+class AdminProblems extends html.Widget
+ content: =>
+ ul ->
+ for problem in *@problems
+ li "#{problem.name} - #{problem.kind} - #{problem.time_limit}"
+
+ a href: (@url_for 'admin.problem.new'),
+ -> text 'Create a problem'
--- /dev/null
+include_rules
--- /dev/null
+html = require "lapis.html"
+
+class AdminProblemEdit extends html.Widget
+ content: =>
+ h1 "Editing '#{@problem.name}'"
+
+ div class: 'content', ->
+ form method: 'POST', ->
+ input type: 'hidden', name: 'csrf_token', value: @csrf_token
+ input type: 'hidden', name: 'problem_id', value: @problem.id
+
+ label for: 'name', 'Problem name'
+ input type: 'text', name: 'name', placeholder: 'Problem name', value: @problem.name, ""
+
+ label for: 'name', 'Short name'
+ input type: 'text', name: 'short_name', placeholder: 'Short URL name', value: @problem.short_name, ""
+
+ label for: 'name', 'Problem description'
+ textarea name: 'description', placeholder: 'Problem description', @problem.description
+
+ label for: 'name', 'Time limit'
+ input type: 'number', value: 500, name: 'time_limit', value: @problem.time_limit, ""
+
+ input type: 'submit', value: 'Update problem'
--- /dev/null
+html = require "lapis.html"
+
+class AdminProblems extends html.Widget
+ content: =>
+ h1 "Create a problem"
+
+ div class: 'content', ->
+ form method: 'POST', ->
+ input type: 'hidden', name: 'csrf_token', value: @csrf_token
+
+ label for: 'name', 'Problem name'
+ input type: 'text', name: 'name', placeholder: 'Problem name', ""
+
+ label for: 'name', 'Short name'
+ input type: 'text', name: 'short_name', placeholder: 'Short URL name', ""
+
+ label for: 'name', 'Problem description'
+ textarea name: 'description', placeholder: 'Problem description', ""
+
+ label for: 'name', 'Time limit'
+ input type: 'number', value: 500, name: 'time_limit', ""
+
+ input type: 'submit', value: 'Create problem'
+
--- /dev/null
+html = require "lapis.html"
+AdminNavbar = require 'views.partials.admin_navbar'
+ErrorList = require 'views.partials.error_list'
+
+class DefaultLayout extends html.Widget
+ content: =>
+ html_5 ->
+ head ->
+ link rel: "stylesheet", href: "/static/css/core.css"
+
+ script type: "text/javascript", src: "/static/js/main.js"
+
+ body ->
+ widget AdminNavbar
+ widget ErrorList
+ @content_for "inner"
--- /dev/null
+html = require 'lapis.html'
+
+class AdminNavigation extends html.Widget
+ content: =>
+ div class: 'navbar', ->
+ div class: 'navbar-logo', ->
+ img src: '/static/imgs/logo.png'
+
+ ul ->
+ a (href: @url_for 'admin.user'), -> li class: { 'selected': @navbar.selected == 0 }, 'Users'
+ a (href: @url_for 'admin.problem'), -> li class: { 'selected': @navbar.selected == 1 }, 'Problems'
+ a (href: @url_for 'admin.submission'), -> li class: { 'selected': @navbar.selected == 2 }, 'Submissions'
+ a (href: @url_for 'admin.competition'), -> li class: { 'selected': @navbar.selected == 3 }, 'Competitions'
--- /dev/null
+html = require 'lapis.html'
+
+class ErrorList extends html.Widget
+ content: =>
+ return unless @errors
+
+ div class: 'error-list', ->
+ ul ->
+ for err in *@errors
+ if err.success then
+ li class: 'success', -> text "Success: #{err.msg}"
+ else
+ li -> text "Error: #{err}"
html = require "lapis.html"
Navbar = require 'views.partials.navbar'
+ErrorList = require 'views.partials.error_list'
class DefaultLayout extends html.Widget
content: =>
html_5 ->
head ->
- link rel: "stylesheet", href: "static/css/core.css"
+ link rel: "stylesheet", href: "/static/css/core.css"
- script type: "text/javascript", src: "static/js/main.js"
+ script type: "text/javascript", src: "/static/js/main.js"
body ->
widget Navbar
+ widget ErrorList
@content_for "inner"
WORKDIR /app
RUN yarn
-COPY ./executer/Tupfile /app/Tupfile
-COPY ./executer/Tuprules.tup /app/Tuprules.tup
COPY ./executer/main.js /app/main.js
COPY ./executer/app /app/app
--- /dev/null
+[ ] Be able to add test cases to problems
+[ ] Be able to delete problems
+[ ] Be able to delete test cases
+[ ] Be able to order test cases
+[ ]
switch (res.status)
when 'SUCCESS'
+ console.log test_case.output, res.output
output = clean_output res.output
expected = create_matchers (clean_output test_case.output)
worked = true
i = 0
for matcher in expected
+ console.log matcher.line, output[i]
unless matcher.test output[i]
worked = false
break
i++
- unless i == output.length
+ if worked && i != output.length
worked = false
run_times[completed] = res.run_time
return { status: 'SUCCESS', output: output, run_time: run_time }
else if res_code == 124 or res_code == 137
bash_shell.kill()
- return { status: 'TIME_LIMIT_EXECEED' }
+ return { status: 'TIME_LIMIT_EXCEEDED' }
else
bash_shell.kill()
return { status: 'BAD_EXECUTION', err: err_output }
test: 'This is test data'
}
- app.post '/submit', (req, res) ->
- console.log req.body.lang, req.body.code
-
- request.post 'http://192.168.0.3:8888/executer/status_update',
- { json: true, form: { request_token: process.env.REQ_SECRET } },
- (err, res, body) ->
- if err
- return console.log err
-
- console.log body.status
-
- res.json {
- id: 'test'
- }
-
app.post '/request', (req, res) ->
cases = JSON.parse req.body.test_cases
job_id = uuid()