├── LICENSE ├── Observables ├── .angular-cli.json ├── .editorconfig ├── .gitignore ├── README.md ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── src │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ └── app.module.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts ├── tsconfig.json └── tslint.json ├── README.md ├── assets ├── managePro.png ├── problem-details.png ├── project-architecture.png └── sign-in.png ├── executor ├── Dockerfile ├── executor_server.py ├── executor_utils.py ├── executor_utils.pyc └── requirements.txt ├── launcher.sh ├── oj-client ├── .editorconfig ├── README.md ├── angular-cli.json ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.json ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── src │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── app.routes.ts │ │ ├── components │ │ │ ├── editor │ │ │ │ ├── editor.component.css │ │ │ │ ├── editor.component.html │ │ │ │ ├── editor.component.spec.ts │ │ │ │ └── editor.component.ts │ │ │ ├── navbar │ │ │ │ ├── navbar.component.css │ │ │ │ ├── navbar.component.html │ │ │ │ ├── navbar.component.spec.ts │ │ │ │ └── navbar.component.ts │ │ │ ├── new-problem │ │ │ │ ├── new-problem.component.css │ │ │ │ ├── new-problem.component.html │ │ │ │ ├── new-problem.component.spec.ts │ │ │ │ └── new-problem.component.ts │ │ │ ├── problem-detail │ │ │ │ ├── problem-detail.component.css │ │ │ │ ├── problem-detail.component.html │ │ │ │ ├── problem-detail.component.spec.ts │ │ │ │ └── problem-detail.component.ts │ │ │ ├── problem-list │ │ │ │ ├── problem-list.component.css │ │ │ │ ├── problem-list.component.html │ │ │ │ ├── problem-list.component.spec.ts │ │ │ │ └── problem-list.component.ts │ │ │ └── profile │ │ │ │ ├── profile.component.css │ │ │ │ ├── profile.component.html │ │ │ │ ├── profile.component.spec.ts │ │ │ │ └── profile.component.ts │ │ ├── mock-problems.ts │ │ ├── models │ │ │ └── problem.model.ts │ │ ├── pipes │ │ │ ├── search.pipe.spec.ts │ │ │ └── search.pipe.ts │ │ └── services │ │ │ ├── auth-guard.service.spec.ts │ │ │ ├── auth-guard.service.ts │ │ │ ├── auth.service.spec.ts │ │ │ ├── auth.service.ts │ │ │ ├── collaboration.service.spec.ts │ │ │ ├── collaboration.service.ts │ │ │ ├── data.service.spec.ts │ │ │ ├── data.service.ts │ │ │ ├── input.service.spec.ts │ │ │ └── input.service.ts │ ├── assets │ │ ├── .gitkeep │ │ └── colors.ts │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ └── tsconfig.json └── tslint.json └── oj-server ├── models └── problemModel.js ├── modules └── redisClient.js ├── package.json ├── routes ├── index.js └── rest.js ├── server.js └── services ├── SocketService.js └── problemService.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 xiyouMc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Observables/.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "observables" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.app.json", 19 | "testTsconfig": "tsconfig.spec.json", 20 | "prefix": "app", 21 | "styles": [ 22 | "styles.css" 23 | ], 24 | "scripts": [], 25 | "environmentSource": "environments/environment.ts", 26 | "environments": { 27 | "dev": "environments/environment.ts", 28 | "prod": "environments/environment.prod.ts" 29 | } 30 | } 31 | ], 32 | "e2e": { 33 | "protractor": { 34 | "config": "./protractor.conf.js" 35 | } 36 | }, 37 | "lint": [ 38 | { 39 | "project": "src/tsconfig.app.json" 40 | }, 41 | { 42 | "project": "src/tsconfig.spec.json" 43 | }, 44 | { 45 | "project": "e2e/tsconfig.e2e.json" 46 | } 47 | ], 48 | "test": { 49 | "karma": { 50 | "config": "./karma.conf.js" 51 | } 52 | }, 53 | "defaults": { 54 | "styleExt": "css", 55 | "component": {} 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Observables/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /Observables/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | 10 | # IDEs and editors 11 | /.idea 12 | .project 13 | .classpath 14 | .c9/ 15 | *.launch 16 | .settings/ 17 | *.sublime-workspace 18 | 19 | # IDE - VSCode 20 | .vscode/* 21 | !.vscode/settings.json 22 | !.vscode/tasks.json 23 | !.vscode/launch.json 24 | !.vscode/extensions.json 25 | 26 | # misc 27 | /.sass-cache 28 | /connect.lock 29 | /coverage/* 30 | /libpeerconnection.log 31 | npm-debug.log 32 | testem.log 33 | /typings 34 | 35 | # e2e 36 | /e2e/*.js 37 | /e2e/*.map 38 | 39 | #System Files 40 | .DS_Store 41 | Thumbs.db 42 | -------------------------------------------------------------------------------- /Observables/README.md: -------------------------------------------------------------------------------- 1 | # Observables 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0-rc.1. 4 | 5 | ## Development server 6 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 7 | 8 | ## Code scaffolding 9 | 10 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`. 11 | 12 | ## Build 13 | 14 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 15 | 16 | ## Running unit tests 17 | 18 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 19 | 20 | ## Running end-to-end tests 21 | 22 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 23 | Before running the tests make sure you are serving the app via `ng serve`. 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /Observables/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { ObservablesPage } from './app.po'; 2 | 3 | describe('observables App', () => { 4 | let page: ObservablesPage; 5 | 6 | beforeEach(() => { 7 | page = new ObservablesPage(); 8 | }); 9 | 10 | it('should display message saying app works', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('app works!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /Observables/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | export class ObservablesPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Observables/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "declaration": false, 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "lib": [ 9 | "es2016" 10 | ], 11 | "outDir": "../dist/out-tsc-e2e", 12 | "module": "commonjs", 13 | "target": "es6", 14 | "types":[ 15 | "jasmine", 16 | "node" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Observables/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | files: [ 19 | { pattern: './src/test.ts', watched: false } 20 | ], 21 | preprocessors: { 22 | './src/test.ts': ['@angular/cli'] 23 | }, 24 | mime: { 25 | 'text/x-typescript': ['ts','tsx'] 26 | }, 27 | coverageIstanbulReporter: { 28 | reports: [ 'html', 'lcovonly' ], 29 | fixWebpackSourcePaths: true 30 | }, 31 | angularCli: { 32 | environment: 'dev' 33 | }, 34 | reporters: config.angularCli && config.angularCli.codeCoverage 35 | ? ['progress', 'coverage-istanbul'] 36 | : ['progress', 'kjhtml'], 37 | port: 9876, 38 | colors: true, 39 | logLevel: config.LOG_INFO, 40 | autoWatch: true, 41 | browsers: ['Chrome'], 42 | singleRun: false 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /Observables/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "observables", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/common": "^2.4.0", 16 | "@angular/compiler": "^2.4.0", 17 | "@angular/core": "^2.4.0", 18 | "@angular/forms": "^2.4.0", 19 | "@angular/http": "^2.4.0", 20 | "@angular/platform-browser": "^2.4.0", 21 | "@angular/platform-browser-dynamic": "^2.4.0", 22 | "@angular/router": "^3.4.0", 23 | "core-js": "^2.4.1", 24 | "rxjs": "^5.1.0", 25 | "zone.js": "^0.7.6" 26 | }, 27 | "devDependencies": { 28 | "@angular/cli": "1.0.0-rc.1", 29 | "@angular/compiler-cli": "^2.4.0", 30 | "@types/jasmine": "2.5.38", 31 | "@types/node": "~6.0.60", 32 | "codelyzer": "~2.0.0", 33 | "jasmine-core": "~2.5.2", 34 | "jasmine-spec-reporter": "~3.2.0", 35 | "karma": "~1.4.1", 36 | "karma-chrome-launcher": "~2.0.0", 37 | "karma-cli": "~1.0.1", 38 | "karma-jasmine": "~1.1.0", 39 | "karma-jasmine-html-reporter": "^0.2.2", 40 | "karma-coverage-istanbul-reporter": "^0.2.0", 41 | "protractor": "~5.1.0", 42 | "ts-node": "~2.0.0", 43 | "tslint": "~4.4.2", 44 | "typescript": "~2.0.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Observables/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | beforeLaunch: function() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | }, 27 | onPrepare() { 28 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /Observables/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/Observables/src/app/app.component.css -------------------------------------------------------------------------------- /Observables/src/app/app.component.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/Observables/src/app/app.component.html -------------------------------------------------------------------------------- /Observables/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | }).compileComponents(); 12 | })); 13 | 14 | it('should create the app', async(() => { 15 | const fixture = TestBed.createComponent(AppComponent); 16 | const app = fixture.debugElement.componentInstance; 17 | expect(app).toBeTruthy(); 18 | })); 19 | 20 | it(`should have as title 'app works!'`, async(() => { 21 | const fixture = TestBed.createComponent(AppComponent); 22 | const app = fixture.debugElement.componentInstance; 23 | expect(app.title).toEqual('app works!'); 24 | })); 25 | 26 | it('should render title in a h1 tag', async(() => { 27 | const fixture = TestBed.createComponent(AppComponent); 28 | fixture.detectChanges(); 29 | const compiled = fixture.debugElement.nativeElement; 30 | expect(compiled.querySelector('h1').textContent).toContain('app works!'); 31 | })); 32 | }); 33 | -------------------------------------------------------------------------------- /Observables/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Observable } from 'rxjs/Rx'; 3 | import { Subject } from 'rxjs/Rx'; 4 | import {Http, Response} from '@angular/http'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.css'] 10 | }) 11 | export class AppComponent { 12 | 13 | constructor(private http: Http) {} 14 | 15 | ngOnInit() :void { 16 | const source$ = Observable.from(['Adam', 'Bill', 'Cow']) 17 | .map(v => v.toUpperCase()) 18 | .map(v => 'I am' + v); 19 | source$.subscribe( 20 | v => console.log(v), 21 | err => console.error(err), 22 | () => console.log('completed') 23 | ); 24 | 25 | } 26 | 27 | // getUser(username) { 28 | // return this.http.get('https://api.github.com/users/' + username); 29 | // } 30 | 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Observables/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { HttpModule } from '@angular/http'; 5 | 6 | import { AppComponent } from './app.component'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | AppComponent 11 | ], 12 | imports: [ 13 | BrowserModule, 14 | FormsModule, 15 | HttpModule 16 | ], 17 | providers: [], 18 | bootstrap: [AppComponent] 19 | }) 20 | export class AppModule { } 21 | -------------------------------------------------------------------------------- /Observables/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/Observables/src/assets/.gitkeep -------------------------------------------------------------------------------- /Observables/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /Observables/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /Observables/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/Observables/src/favicon.ico -------------------------------------------------------------------------------- /Observables/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Observables 6 | 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | -------------------------------------------------------------------------------- /Observables/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule); 12 | -------------------------------------------------------------------------------- /Observables/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/set'; 35 | 36 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 37 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 38 | 39 | /** IE10 and IE11 requires the following to support `@angular/animation`. */ 40 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 41 | 42 | 43 | /** Evergreen browsers require these. **/ 44 | import 'core-js/es6/reflect'; 45 | import 'core-js/es7/reflect'; 46 | 47 | 48 | /** ALL Firefox browsers require the following to support `@angular/animation`. **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | -------------------------------------------------------------------------------- /Observables/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Observables/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare var __karma__: any; 17 | declare var require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /Observables/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "declaration": false, 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "lib": [ 9 | "es2016", 10 | "dom" 11 | ], 12 | "outDir": "../out-tsc/app", 13 | "target": "es5", 14 | "module": "es2015", 15 | "baseUrl": "", 16 | "types": [] 17 | }, 18 | "exclude": [ 19 | "test.ts", 20 | "**/*.spec.ts" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /Observables/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "declaration": false, 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "lib": [ 9 | "es2016" 10 | ], 11 | "outDir": "../out-tsc/spec", 12 | "module": "commonjs", 13 | "target": "es6", 14 | "baseUrl": "", 15 | "types": [ 16 | "jasmine", 17 | "node" 18 | ] 19 | }, 20 | "files": [ 21 | "test.ts" 22 | ], 23 | "include": [ 24 | "**/*.spec.ts" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /Observables/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: { 3 | id: string; 4 | }; 5 | -------------------------------------------------------------------------------- /Observables/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "lib": [ 12 | "es2016", 13 | "dom" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Observables/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "callable-types": true, 7 | "class-name": true, 8 | "comment-format": [ 9 | true, 10 | "check-space" 11 | ], 12 | "curly": true, 13 | "eofline": true, 14 | "forin": true, 15 | "import-blacklist": [true, "rxjs"], 16 | "import-spacing": true, 17 | "indent": [ 18 | true, 19 | "spaces" 20 | ], 21 | "interface-over-type-literal": true, 22 | "label-position": true, 23 | "max-line-length": [ 24 | true, 25 | 140 26 | ], 27 | "member-access": false, 28 | "member-ordering": [ 29 | true, 30 | "static-before-instance", 31 | "variables-before-functions" 32 | ], 33 | "no-arg": true, 34 | "no-bitwise": true, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-construct": true, 44 | "no-debugger": true, 45 | "no-duplicate-variable": true, 46 | "no-empty": false, 47 | "no-empty-interface": true, 48 | "no-eval": true, 49 | "no-inferrable-types": [true, "ignore-params"], 50 | "no-shadowed-variable": true, 51 | "no-string-literal": false, 52 | "no-string-throw": true, 53 | "no-switch-case-fall-through": true, 54 | "no-trailing-whitespace": true, 55 | "no-unused-expression": true, 56 | "no-use-before-declare": true, 57 | "no-var-keyword": true, 58 | "object-literal-sort-keys": false, 59 | "one-line": [ 60 | true, 61 | "check-open-brace", 62 | "check-catch", 63 | "check-else", 64 | "check-whitespace" 65 | ], 66 | "prefer-const": true, 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "radix": true, 72 | "semicolon": [ 73 | "always" 74 | ], 75 | "triple-equals": [ 76 | true, 77 | "allow-null-check" 78 | ], 79 | "typedef-whitespace": [ 80 | true, 81 | { 82 | "call-signature": "nospace", 83 | "index-signature": "nospace", 84 | "parameter": "nospace", 85 | "property-declaration": "nospace", 86 | "variable-declaration": "nospace" 87 | } 88 | ], 89 | "typeof-compare": true, 90 | "unified-signatures": true, 91 | "variable-name": false, 92 | "whitespace": [ 93 | true, 94 | "check-branch", 95 | "check-decl", 96 | "check-operator", 97 | "check-separator", 98 | "check-type" 99 | ], 100 | 101 | "directive-selector": [true, "attribute", "app", "camelCase"], 102 | "component-selector": [true, "element", "app", "kebab-case"], 103 | "use-input-property-decorator": true, 104 | "use-output-property-decorator": true, 105 | "use-host-property-decorator": true, 106 | "no-input-rename": true, 107 | "no-output-rename": true, 108 | "use-life-cycle-interface": true, 109 | "use-pipe-transform-interface": true, 110 | "component-class-suffix": true, 111 | "directive-class-suffix": true, 112 | "no-access-missing-member": true, 113 | "templates-use-public": true, 114 | "invoke-injectable": true 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Collaborative Online Judge System 2 | This is a project to implement a web-based collaborative code editor which supports multiple users editing simultaneously. 3 | I designed a single-page web application for coding, running, and compiling problems. 4 | To refactored and improved system throughput, I used decoupling services based on RESTful API and loaded balancing by Nginx. 5 | 6 | ## Tech Stack 7 | Frontend: Angular2 + TypeScript + Bootstrap + Auth0 + ACE 8 | Backend: Socket.io + Express + MongoDB + Redis + Python + Flask + AWS + Nginx + PM2 9 | 10 | ## Project Architecture 11 | 12 | 13 | ## Demo 14 | #### Before sign-in 15 | ![image1](/assets/sign-in.png) 16 | #### After authentication 17 | ![image2](/assets/managePro.png) 18 | #### Code excutor 19 | ![image3](/assets/problem-details.png) 20 | -------------------------------------------------------------------------------- /assets/managePro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/assets/managePro.png -------------------------------------------------------------------------------- /assets/problem-details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/assets/problem-details.png -------------------------------------------------------------------------------- /assets/project-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/assets/project-architecture.png -------------------------------------------------------------------------------- /assets/sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/assets/sign-in.png -------------------------------------------------------------------------------- /executor/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | MAINTAINER Chan Mao 4 | 5 | RUN apt-get update 6 | RUN apt-get install -y gcc 7 | RUN apt-get install -y g++ 8 | RUN apt-get install -y openjdk-8-jdk 9 | RUN apt-get install -y python 10 | -------------------------------------------------------------------------------- /executor/executor_server.py: -------------------------------------------------------------------------------- 1 | import executor_utils as eu 2 | 3 | import json 4 | 5 | from flask import Flask 6 | from flask import jsonify 7 | from flask import request 8 | 9 | app = Flask(__name__) 10 | 11 | @app.route("/") 12 | def hello(): 13 | return "Hehe" 14 | 15 | @app.route("/build_and_run", methods=["POST"]) 16 | def build_and_run(): 17 | print "Got called: %s" % (request.data) 18 | data = json.loads(request.data) 19 | 20 | if 'code' not in data or 'lang' not in data: 21 | return "You should provide both 'code' and 'lang'" 22 | code = data['code'] 23 | lang = data['lang'] 24 | 25 | print "API got called with code: %s in %s" % (code, lang) 26 | 27 | result = eu.build_and_run(code, lang) 28 | return jsonify(result) 29 | 30 | if __name__ == "__main__": 31 | eu.load_image() 32 | app.run() 33 | -------------------------------------------------------------------------------- /executor/executor_utils.py: -------------------------------------------------------------------------------- 1 | import docker 2 | import os 3 | import shutil 4 | import uuid 5 | 6 | from docker.errors import * 7 | 8 | IMAGE_NAME = "qianmao/cs503_coj_demo_1" 9 | 10 | CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) 11 | TEMP_BUILD_DIR = "%s/tmp/" % CURRENT_DIR 12 | 13 | SOURCE_FILE_NAMES = { 14 | "java" : "Example.java", 15 | 'python' : 'example.py' 16 | } 17 | 18 | BINARY_NAMES = { 19 | "java" : "Example", 20 | 'python' : 'example.py' 21 | } 22 | 23 | BUILD_COMMANDS = { 24 | "java" : "javac", 25 | "python" : "python" 26 | } 27 | 28 | EXECUTE_COMMANDS = { 29 | "java" : "java", 30 | "python" : "python" 31 | } 32 | 33 | client = docker.from_env() 34 | 35 | def load_image(): 36 | try: 37 | client.images.get(IMAGE_NAME) 38 | except ImageNotFound: 39 | print "Image not found locally. Loading from Dockerhub..." 40 | client.images.pull(IMAGE_NAME) 41 | except APIError: 42 | print "Image not found locally. DockerHub is not accessible." 43 | return 44 | print "Image:[%s] loaded" % IMAGE_NAME 45 | 46 | def build_and_run(code, lang): 47 | result = {'build': None, 'run': None, 'error': None} 48 | 49 | source_file_parent_dir_name = uuid.uuid4() 50 | source_file_host_dir = "%s/%s" % (TEMP_BUILD_DIR, source_file_parent_dir_name) 51 | source_file_guest_dir = "/test/%s" % (source_file_parent_dir_name) 52 | make_dir(source_file_host_dir) 53 | 54 | with open('%s/%s' % (source_file_host_dir, SOURCE_FILE_NAMES[lang]), 'w') as source_file: 55 | source_file.write(code) 56 | 57 | try: 58 | client.containers.run( 59 | image=IMAGE_NAME, 60 | command="%s %s" % (BUILD_COMMANDS[lang], SOURCE_FILE_NAMES[lang]), 61 | volumes={source_file_host_dir: {'bind': source_file_guest_dir, 'mode': 'rw'}}, 62 | working_dir=source_file_guest_dir) 63 | print "Source built." 64 | result['build'] = 'OK' 65 | except ContainerError as e: 66 | print "Build failed." 67 | result['build'] = e.stderr 68 | shutil.rmtree(source_file_host_dir) 69 | return result 70 | 71 | try: 72 | log = client.containers.run( 73 | image=IMAGE_NAME, 74 | command="%s %s" % (EXECUTE_COMMANDS[lang], BINARY_NAMES[lang]), 75 | volumes={source_file_host_dir: {'bind': source_file_guest_dir, 'mode': 'rw'}}, 76 | working_dir=source_file_guest_dir) 77 | print "Executed." 78 | result['run'] = log 79 | except ContainerError as e: 80 | print "Execution failed." 81 | result['run'] = e.stderr 82 | shutil.rmtree(source_file_host_dir) 83 | return result 84 | 85 | shutil.rmtree(source_file_host_dir) 86 | return result 87 | 88 | def make_dir(dir): 89 | try: 90 | os.mkdir(dir) 91 | print "Temp build directory [%s] created." % dir 92 | except OSError: 93 | print "Temp build directory [%s] exists." % dir 94 | -------------------------------------------------------------------------------- /executor/executor_utils.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/executor/executor_utils.pyc -------------------------------------------------------------------------------- /executor/requirements.txt: -------------------------------------------------------------------------------- 1 | docker 2 | Flask 3 | -------------------------------------------------------------------------------- /launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | fuser -k 3000/tcp 3 | fuser -k 5000/tcp 4 | 5 | service redis_6379 start 6 | cd ./oj-server 7 | npm install 8 | nodemon server.js & 9 | cd ../oj-client 10 | npm install 11 | ng build --watch & 12 | cd ../executor 13 | pip install -r requirements.txt 14 | python executor_server.py & 15 | 16 | echo "==================================================" 17 | read -p "PRESS [ENTER] TO TERMINATE PROCESSES." PRESSKEY 18 | 19 | fuser -k 3000/tcp 20 | fuser -k 5000/tcp 21 | service redis_6379 stop 22 | -------------------------------------------------------------------------------- /oj-client/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /oj-client/README.md: -------------------------------------------------------------------------------- 1 | # OjClient 2 | 3 | This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.28.3. 4 | 5 | ## Development server 6 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 7 | 8 | ## Code scaffolding 9 | 10 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`. 11 | 12 | ## Build 13 | 14 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 15 | 16 | ## Running unit tests 17 | 18 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 19 | 20 | ## Running end-to-end tests 21 | 22 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 23 | Before running the tests make sure you are serving the app via `ng serve`. 24 | 25 | ## Deploying to GitHub Pages 26 | 27 | Run `ng github-pages:deploy` to deploy to GitHub Pages. 28 | 29 | ## Further help 30 | 31 | To get more help on the `angular-cli` use `ng help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 32 | -------------------------------------------------------------------------------- /oj-client/angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": { 3 | "version": "1.0.0-beta.28.3", 4 | "name": "oj-client" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "../public", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.json", 19 | "prefix": "app", 20 | "styles": [ 21 | "styles.css", 22 | "../node_modules/bootstrap/dist/css/bootstrap.css" 23 | ], 24 | "scripts": [ 25 | "../node_modules/jquery/dist/jquery.js", 26 | "../node_modules/bootstrap/dist/js/bootstrap.js", 27 | "../node_modules/ace-builds/src-min-noconflict/ace.js", 28 | "../node_modules/ace-builds/src-min-noconflict/theme-eclipse.js", 29 | "../node_modules/ace-builds/src-min-noconflict/mode-java.js", 30 | "../node_modules/ace-builds/src-min-noconflict/mode-c_cpp.js", 31 | "../node_modules/ace-builds/src-min-noconflict/mode-python.js" 32 | 33 | 34 | ], 35 | "environmentSource": "environments/environment.ts", 36 | "environments": { 37 | "dev": "environments/environment.ts", 38 | "prod": "environments/environment.prod.ts" 39 | } 40 | } 41 | ], 42 | "e2e": { 43 | "protractor": { 44 | "config": "./protractor.conf.js" 45 | } 46 | }, 47 | "lint": [ 48 | { 49 | "files": "src/**/*.ts", 50 | "project": "src/tsconfig.json" 51 | }, 52 | { 53 | "files": "e2e/**/*.ts", 54 | "project": "e2e/tsconfig.json" 55 | } 56 | ], 57 | "test": { 58 | "karma": { 59 | "config": "./karma.conf.js" 60 | } 61 | }, 62 | "defaults": { 63 | "styleExt": "css", 64 | "prefixInterfaces": false, 65 | "inline": { 66 | "style": false, 67 | "template": false 68 | }, 69 | "spec": { 70 | "class": false, 71 | "component": true, 72 | "directive": true, 73 | "module": false, 74 | "pipe": true, 75 | "service": true 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /oj-client/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { OjClientPage } from './app.po'; 2 | 3 | describe('oj-client App', function() { 4 | let page: OjClientPage; 5 | 6 | beforeEach(() => { 7 | page = new OjClientPage(); 8 | }); 9 | 10 | it('should display message saying app works', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('app works!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /oj-client/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | export class OjClientPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /oj-client/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "outDir": "../dist/out-tsc-e2e", 10 | "sourceMap": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "../node_modules/@types" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /oj-client/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', 'angular-cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-remap-istanbul'), 12 | require('angular-cli/plugins/karma') 13 | ], 14 | files: [ 15 | { pattern: './src/test.ts', watched: false } 16 | ], 17 | preprocessors: { 18 | './src/test.ts': ['angular-cli'] 19 | }, 20 | mime: { 21 | 'text/x-typescript': ['ts','tsx'] 22 | }, 23 | remapIstanbulReporter: { 24 | reports: { 25 | html: 'coverage', 26 | lcovonly: './coverage/coverage.lcov' 27 | } 28 | }, 29 | angularCli: { 30 | config: './angular-cli.json', 31 | environment: 'dev' 32 | }, 33 | reporters: config.angularCli && config.angularCli.codeCoverage 34 | ? ['progress', 'karma-remap-istanbul'] 35 | : ['progress'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /oj-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oj-client", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "angular-cli": {}, 6 | "scripts": { 7 | "ng": "ng", 8 | "start": "ng serve", 9 | "test": "ng test", 10 | "pree2e": "webdriver-manager update --standalone false --gecko false", 11 | "e2e": "protractor" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/common": "^2.3.1", 16 | "@angular/compiler": "^2.3.1", 17 | "@angular/core": "^2.3.1", 18 | "@angular/forms": "^2.3.1", 19 | "@angular/http": "^2.3.1", 20 | "@angular/platform-browser": "^2.3.1", 21 | "@angular/platform-browser-dynamic": "^2.3.1", 22 | "@angular/router": "^3.3.1", 23 | "ace-builds": "^1.2.6", 24 | "angular2-jwt": "^0.1.28", 25 | "bootstrap": "^3.3.7", 26 | "core-js": "^2.4.1", 27 | "jquery": "^3.1.1", 28 | "rxjs": "^5.0.1", 29 | "ts-helpers": "^1.1.1", 30 | "zone.js": "^0.7.2" 31 | }, 32 | "devDependencies": { 33 | "@angular/cli": "1.0.0-rc.1", 34 | "@angular/compiler-cli": "^2.3.1", 35 | "@types/jasmine": "2.5.38", 36 | "@types/node": "^6.0.42", 37 | "codelyzer": "~2.0.0-beta.1", 38 | "jasmine-core": "2.5.2", 39 | "jasmine-spec-reporter": "2.5.0", 40 | "karma": "1.2.0", 41 | "karma-chrome-launcher": "^2.0.0", 42 | "karma-cli": "^1.0.1", 43 | "karma-jasmine": "^1.0.2", 44 | "karma-remap-istanbul": "^0.2.1", 45 | "protractor": "~4.0.13", 46 | "ts-node": "1.2.1", 47 | "tslint": "^4.3.0", 48 | "typescript": "~2.0.3" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /oj-client/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | /*global jasmine */ 5 | var SpecReporter = require('jasmine-spec-reporter'); 6 | 7 | exports.config = { 8 | allScriptsTimeout: 11000, 9 | specs: [ 10 | './e2e/**/*.e2e-spec.ts' 11 | ], 12 | capabilities: { 13 | 'browserName': 'chrome' 14 | }, 15 | directConnect: true, 16 | baseUrl: 'http://localhost:4200/', 17 | framework: 'jasmine', 18 | jasmineNodeOpts: { 19 | showColors: true, 20 | defaultTimeoutInterval: 30000, 21 | print: function() {} 22 | }, 23 | useAllAngular2AppRoots: true, 24 | beforeLaunch: function() { 25 | require('ts-node').register({ 26 | project: 'e2e' 27 | }); 28 | }, 29 | onPrepare: function() { 30 | jasmine.getEnv().addReporter(new SpecReporter()); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /oj-client/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/oj-client/src/app/app.component.css -------------------------------------------------------------------------------- /oj-client/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /oj-client/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | 3 | import { TestBed, async } from '@angular/core/testing'; 4 | import { AppComponent } from './app.component'; 5 | 6 | describe('AppComponent', () => { 7 | beforeEach(() => { 8 | TestBed.configureTestingModule({ 9 | declarations: [ 10 | AppComponent 11 | ], 12 | }); 13 | TestBed.compileComponents(); 14 | }); 15 | 16 | it('should create the app', async(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app).toBeTruthy(); 20 | })); 21 | 22 | it(`should have as title 'app works!'`, async(() => { 23 | const fixture = TestBed.createComponent(AppComponent); 24 | const app = fixture.debugElement.componentInstance; 25 | expect(app.title).toEqual('app works!'); 26 | })); 27 | 28 | it('should render title in a h1 tag', async(() => { 29 | const fixture = TestBed.createComponent(AppComponent); 30 | fixture.detectChanges(); 31 | const compiled = fixture.debugElement.nativeElement; 32 | expect(compiled.querySelector('h1').textContent).toContain('app works!'); 33 | })); 34 | }); 35 | -------------------------------------------------------------------------------- /oj-client/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'app works again!'; 10 | } 11 | -------------------------------------------------------------------------------- /oj-client/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { ReactiveFormsModule } from '@angular/forms'; 5 | import { HttpModule } from '@angular/http'; 6 | 7 | import { routing } from "./app.routes"; 8 | 9 | import { AppComponent } from './app.component'; 10 | import { ProblemListComponent } from './components/problem-list/problem-list.component'; 11 | import { ProblemDetailComponent } from './components/problem-detail/problem-detail.component'; 12 | 13 | import { DataService } from "./services/data.service"; 14 | import { AuthService } from "./services/auth.service"; 15 | import { AuthGuardService } from "./services/auth-guard.service"; 16 | import { CollaborationService } from "./services/collaboration.service"; 17 | import { InputService } from "./services/input.service"; 18 | 19 | import { NewProblemComponent } from './components/new-problem/new-problem.component'; 20 | import { NavbarComponent } from './components/navbar/navbar.component'; 21 | import { ProfileComponent } from './components/profile/profile.component'; 22 | import { EditorComponent } from './components/editor/editor.component'; 23 | import { SearchPipe } from './pipes/search.pipe'; 24 | 25 | @NgModule({ 26 | declarations: [ 27 | AppComponent, 28 | ProblemListComponent, 29 | ProblemDetailComponent, 30 | NewProblemComponent, 31 | NavbarComponent, 32 | ProfileComponent, 33 | EditorComponent, 34 | SearchPipe 35 | ], 36 | imports: [ 37 | BrowserModule, 38 | FormsModule, 39 | ReactiveFormsModule, 40 | HttpModule, 41 | routing 42 | ], 43 | providers: [{ 44 | provide: "data", 45 | useClass: DataService 46 | }, { 47 | provide: "auth", 48 | useClass: AuthService 49 | }, { 50 | provide: "authGuard", 51 | useClass: AuthGuardService 52 | }, { 53 | provide: "collaboration", 54 | useClass: CollaborationService 55 | }, { 56 | provide: "input", 57 | useClass: InputService 58 | }], 59 | bootstrap: [AppComponent] 60 | }) 61 | export class AppModule { } 62 | -------------------------------------------------------------------------------- /oj-client/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes, RouterModule } from "@angular/router"; 2 | import { ProblemListComponent } from './components/problem-list/problem-list.component'; 3 | import { ProblemDetailComponent } from './components/problem-detail/problem-detail.component'; 4 | import { ProfileComponent } from './components/profile/profile.component'; 5 | 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | redirectTo: 'problems', 11 | pathMatch: 'full' 12 | }, 13 | { 14 | path: 'problems', 15 | component: ProblemListComponent 16 | }, 17 | { 18 | path: 'problems/:id', 19 | component: ProblemDetailComponent 20 | }, 21 | { 22 | path: 'profile', 23 | canActivate: ['authGuard'], 24 | component: ProfileComponent 25 | }, { 26 | path: '**', 27 | redirectTo: 'problems' 28 | } 29 | ]; 30 | 31 | export const routing = RouterModule.forRoot(routes); 32 | -------------------------------------------------------------------------------- /oj-client/src/app/components/editor/editor.component.css: -------------------------------------------------------------------------------- 1 | @media screen { 2 | #editor { 3 | height: 600px; 4 | } 5 | .lang-select { 6 | width: 100px; 7 | margin-right: 10px; 8 | } 9 | header .btn { 10 | margin: 0 5px; 11 | } 12 | footer .btn { 13 | margin: 0 5px; 14 | } 15 | .editor-footer, .editor-header { 16 | margin: 10px 0; 17 | } 18 | .cursor { 19 | /*position:absolute;*/ 20 | background: rgba(0, 250, 0, 0.5); 21 | z-index: 40; 22 | width: 2px!important 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /oj-client/src/app/components/editor/editor.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 8 | ​ 9 | 10 | 11 | 14 | ​ 15 | 16 | 33 |
34 | 35 |
36 |
37 |
38 | ​ 39 |
40 | {{output}} 41 |
42 | ​ 43 | 46 |
47 | -------------------------------------------------------------------------------- /oj-client/src/app/components/editor/editor.component.spec.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/oj-client/src/app/components/editor/editor.component.spec.ts -------------------------------------------------------------------------------- /oj-client/src/app/components/editor/editor.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit, Inject} from '@angular/core'; 2 | 3 | import {ActivatedRoute, Params} from '@angular/router'; 4 | 5 | declare var ace: any; 6 | 7 | @Component({ 8 | selector: 'app-editor', 9 | templateUrl: './editor.component.html', 10 | styleUrls: ['./editor.component.css'] 11 | }) 12 | export class EditorComponent implements OnInit { 13 | 14 | editor: any; 15 | 16 | public languages: string[] = ['Java', 'C++', 'Python']; 17 | language: string = 'Java'; // default 18 | 19 | sessionId: string; 20 | 21 | output: string; 22 | 23 | defaultContent = { 24 | 'Java': `public class Example { 25 | public static void main(String[] args) { 26 | // Type your Java code here 27 | } 28 | }`, 29 | 'C++': `#include 30 | using namespace std; 31 | ​ 32 | int main() { 33 | // Type your C++ code here 34 | return 0; 35 | }`, 36 | 'Python': `class Solution: 37 | def example(): 38 | # Write your Python code here` 39 | }; 40 | 41 | constructor(@Inject('collaboration') private collaboration, 42 | @Inject('data') private data, 43 | private route: ActivatedRoute) { 44 | 45 | } 46 | 47 | ngOnInit() { 48 | this.route.params 49 | .subscribe(params => { 50 | this.sessionId = params['id']; 51 | this.initEditor(); 52 | }); 53 | } 54 | 55 | initEditor() { 56 | this.editor = ace.edit('editor'); 57 | this.editor.setTheme('ace/theme/eclipse'); 58 | this.resetEditor(); 59 | this.editor.$blockScrolling = Infinity; 60 | 61 | document.getElementsByTagName('textarea')[0].focus(); 62 | 63 | this.collaboration.init(this.editor, this.sessionId); 64 | this.editor.lastAppliedChange = null; 65 | 66 | this.editor.on('change', (e) => { 67 | console.log('editor changes: ' + JSON.stringify(e)); 68 | if (this.editor.lastAppliedChange != e) { 69 | this.collaboration.change(JSON.stringify(e)); 70 | } 71 | }); 72 | 73 | this.editor.getSession().getSelection().on("changeCursor", () => { 74 | let cursor = this.editor.getSession().getSelection().getCursor(); 75 | console.log('cursor moves: ' + JSON.stringify(cursor)); 76 | this.collaboration.cursorMove(JSON.stringify(cursor)); 77 | }); 78 | 79 | this.collaboration.restoreBuffer(); 80 | } 81 | 82 | setLanguage(language: string): void { 83 | this.language = language; 84 | this.resetEditor(); 85 | } 86 | 87 | resetEditor(): void { 88 | this.editor.getSession().setMode('ace/mode/' + this.language.toLowerCase()); 89 | this.editor.setValue(this.defaultContent[this.language]); 90 | this.output = ''; 91 | } 92 | 93 | submit(): void { 94 | let userCode = this.editor.getValue(); 95 | let data = { 96 | user_code: userCode, 97 | lang: this.language.toLowerCase() 98 | }; 99 | this.data.buildAndRun(data) 100 | .then(res => this.output = res.text); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /oj-client/src/app/components/navbar/navbar.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/oj-client/src/app/components/navbar/navbar.component.css -------------------------------------------------------------------------------- /oj-client/src/app/components/navbar/navbar.component.html: -------------------------------------------------------------------------------- 1 |
2 | 42 |
43 | -------------------------------------------------------------------------------- /oj-client/src/app/components/navbar/navbar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NavbarComponent } from './navbar.component'; 4 | 5 | describe('NavbarComponent', () => { 6 | let component: NavbarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NavbarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NavbarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /oj-client/src/app/components/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject, OnDestroy } from '@angular/core'; 2 | import { FormControl } from '@angular/forms'; 3 | import { Subscription } from 'rxjs/Subscription'; 4 | import { Router } from '@angular/router'; 5 | import 'rxjs/add/operator/debounceTime'; 6 | 7 | @Component({ 8 | selector: 'app-navbar', 9 | templateUrl: './navbar.component.html', 10 | styleUrls: ['./navbar.component.css'] 11 | }) 12 | export class NavbarComponent implements OnInit { 13 | 14 | title = "COJ"; 15 | 16 | username = ""; 17 | 18 | searchBox: FormControl = new FormControl(); 19 | subscription: Subscription; 20 | 21 | constructor(@Inject('auth') private auth, 22 | @Inject('input') private input, 23 | private router: Router) { } 24 | 25 | ngOnInit() { 26 | if (this.auth.authenticated()) { 27 | this.username = this.auth.getProfile().nickname; 28 | } 29 | 30 | this.subscription = this.searchBox 31 | .valueChanges 32 | .debounceTime(200) 33 | .subscribe( 34 | term => { 35 | this.input.changeInput(term); 36 | } 37 | ); 38 | } 39 | 40 | ngOnDestroy() { 41 | this.subscription.unsubscribe(); 42 | } 43 | 44 | searchProblem(): void { 45 | this.router.navigate(['/problems']); 46 | } 47 | 48 | login(): void { 49 | this.auth.login() 50 | .then(profile => this.username = profile.nickname); 51 | } 52 | 53 | logout(): void { 54 | this.auth.logout(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /oj-client/src/app/components/new-problem/new-problem.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/oj-client/src/app/components/new-problem/new-problem.component.css -------------------------------------------------------------------------------- /oj-client/src/app/components/new-problem/new-problem.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 7 |
8 |
9 | 10 | 12 |
13 |
14 | 15 | 21 |
22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /oj-client/src/app/components/new-problem/new-problem.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NewProblemComponent } from './new-problem.component'; 4 | 5 | describe('NewProblemComponent', () => { 6 | let component: NewProblemComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NewProblemComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NewProblemComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /oj-client/src/app/components/new-problem/new-problem.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject } from '@angular/core'; 2 | import { Problem } from "../../models/problem.model"; 3 | 4 | const DEFAULT_PROBLEM: Problem = Object.freeze({ 5 | id: 0, 6 | name: "", 7 | desc: "", 8 | difficulty: "Easy" 9 | }); 10 | 11 | @Component({ 12 | selector: 'app-new-problem', 13 | templateUrl: './new-problem.component.html', 14 | styleUrls: ['./new-problem.component.css'] 15 | }) 16 | export class NewProblemComponent implements OnInit { 17 | 18 | public difficulties = ["Easy", "Medium", "Hard", "Super"]; 19 | 20 | newProblem: Problem = Object.assign({}, DEFAULT_PROBLEM); 21 | 22 | constructor(@Inject("data") private data, 23 | @Inject("authGuard") private authGuard ) { } 24 | 25 | ngOnInit() { 26 | } 27 | 28 | addProblem(): void { 29 | this.data.addProblem(this.newProblem) 30 | .catch(error => console.log(error._body)); 31 | this.newProblem = Object.assign({}, DEFAULT_PROBLEM); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /oj-client/src/app/components/problem-detail/problem-detail.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/oj-client/src/app/components/problem-detail/problem-detail.component.css -------------------------------------------------------------------------------- /oj-client/src/app/components/problem-detail/problem-detail.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

5 | {{problem.id}}. {{problem.name}} 6 |

7 |

8 | {{problem.desc}} 9 |

10 |
11 |
12 |
13 | 16 |
17 | -------------------------------------------------------------------------------- /oj-client/src/app/components/problem-detail/problem-detail.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { DebugElement } from '@angular/core'; 5 | 6 | import { ProblemDetailComponent } from './problem-detail.component'; 7 | 8 | describe('ProblemDetailComponent', () => { 9 | let component: ProblemDetailComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ ProblemDetailComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(ProblemDetailComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /oj-client/src/app/components/problem-detail/problem-detail.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject } from '@angular/core'; 2 | import { Problem } from "../../models/problem.model"; 3 | import { ActivatedRoute} from "@angular/router"; 4 | 5 | @Component({ 6 | selector: 'app-problem-detail', 7 | templateUrl: './problem-detail.component.html', 8 | styleUrls: ['./problem-detail.component.css'] 9 | }) 10 | export class ProblemDetailComponent implements OnInit { 11 | 12 | problem: Problem; 13 | 14 | constructor( 15 | private route: ActivatedRoute, 16 | @Inject("data") private data 17 | ) { } 18 | 19 | ngOnInit() { 20 | this.route.params.subscribe(params => { 21 | this.data.getProblem(+params['id']) 22 | .then(problem => this.problem = problem); 23 | }); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /oj-client/src/app/components/problem-list/problem-list.component.css: -------------------------------------------------------------------------------- 1 | .difficulty { 2 | min-width: 65px; 3 | margin-right: 10px; 4 | } 5 | 6 | .label.difficulty { 7 | padding-top: 0.6em; 8 | color: #fbfdfa; 9 | font-size: 12px; 10 | } 11 | 12 | .title { 13 | font-size: 1.2em; 14 | } 15 | 16 | .diff-easy { 17 | background-color: #42ebf4; 18 | } 19 | 20 | .diff-medium { 21 | background-color: #92cf5c; 22 | } 23 | 24 | .diff-hard { 25 | background-color: #dd0d1e; 26 | } 27 | 28 | .diff-super { 29 | background-color: #8d16e2; 30 | } 31 | -------------------------------------------------------------------------------- /oj-client/src/app/components/problem-list/problem-list.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
My birthday is {{birthday | date: 'fullDate' | uppercase }}
4 |
My account balance is {{money | currency }}
5 |
My credit card Apt is {{Apr | percent }}
6 | 12 |
13 | -------------------------------------------------------------------------------- /oj-client/src/app/components/problem-list/problem-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { DebugElement } from '@angular/core'; 5 | 6 | import { ProblemListComponent } from './problem-list.component'; 7 | 8 | describe('ProblemListComponent', () => { 9 | let component: ProblemListComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ ProblemListComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(ProblemListComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /oj-client/src/app/components/problem-list/problem-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject } from '@angular/core'; 2 | import { Problem } from "../../models/problem.model"; 3 | import { Subscription } from 'rxjs/Subscription'; 4 | 5 | 6 | @Component({ 7 | selector: 'app-problem-list', 8 | templateUrl: './problem-list.component.html', 9 | styleUrls: ['./problem-list.component.css'] 10 | }) 11 | export class ProblemListComponent implements OnInit { 12 | 13 | problems: Problem[] = []; 14 | subscriptionProblems: Subscription; 15 | 16 | birthday = new Date(); 17 | money = 25; 18 | Apr = 0.21; 19 | 20 | searchTerm: string = ''; 21 | subscriptionInput: Subscription; 22 | 23 | constructor(@Inject("data") private data, 24 | @Inject("input") private input) { } 25 | 26 | ngOnInit() { 27 | this.getProblems(); 28 | this.getSearchTerm(); 29 | } 30 | 31 | getProblems(): void { 32 | this.subscriptionProblems = this.data.getProblems() 33 | .subscribe(problems => this.problems = problems); 34 | } 35 | 36 | getSearchTerm(): void { 37 | this.subscriptionInput = this.input.getInput() 38 | .subscribe( 39 | inputTerm => this.searchTerm = inputTerm 40 | ); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /oj-client/src/app/components/profile/profile.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/oj-client/src/app/components/profile/profile.component.css -------------------------------------------------------------------------------- /oj-client/src/app/components/profile/profile.component.html: -------------------------------------------------------------------------------- 1 |
2 | 6 | 7 |
8 | 9 |
10 |
11 |
12 | 13 | 15 |
16 |
17 | 18 | 20 |
21 |
22 |
23 | 26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /oj-client/src/app/components/profile/profile.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProfileComponent } from './profile.component'; 4 | 5 | describe('ProfileComponent', () => { 6 | let component: ProfileComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ProfileComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ProfileComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /oj-client/src/app/components/profile/profile.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-profile', 5 | templateUrl: './profile.component.html', 6 | styleUrls: ['./profile.component.css'] 7 | }) 8 | export class ProfileComponent implements OnInit { 9 | email: string = ''; 10 | username: string = ''; 11 | 12 | constructor(@Inject('auth') private auth) { } 13 | 14 | ngOnInit() { 15 | let profile = this.auth.getProfile(); 16 | this.email = profile.email; 17 | this.username = profile.nickname; 18 | } 19 | 20 | resetPassword() { 21 | this.auth.resetPassword(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /oj-client/src/app/mock-problems.ts: -------------------------------------------------------------------------------- 1 | import { Problem } from "./models/problem.model"; 2 | 3 | export const PROBLEMS: Problem[] = [ 4 | { 5 | id: 1, 6 | name: "Two Sum", 7 | desc: `Given an array of integers, find two numbers such that they add up to a specific target number. 8 | 9 | The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are NOT zero-based.`, 10 | difficulty: "easy" 11 | }, 12 | { 13 | id: 2, 14 | name: "3Sum", 15 | desc: `Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.`, 16 | difficulty: "medium" 17 | }, 18 | { 19 | id: 3, 20 | name: "4Sum", 21 | desc: `Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? 22 | 23 | Find all unique quadruplets in the array which gives the sum of target.`, 24 | difficulty: "medium" 25 | }, 26 | { 27 | id: 4, 28 | name: "Triangle Count", 29 | desc: `Given an array of integers, how many three numbers can be found in the array, so that we can build an triangle whose three edges length is the three numbers that we find?`, 30 | difficulty: "hard" 31 | }, 32 | { 33 | id: 5, 34 | name: "Sliding Window Maximum", 35 | desc: `Given an array of n integer with duplicate number, and a moving window(size k), move the window at each iteration from the start of the array, find the maximum number inside the window at each moving.`, 36 | difficulty: "super" 37 | } 38 | ]; 39 | -------------------------------------------------------------------------------- /oj-client/src/app/models/problem.model.ts: -------------------------------------------------------------------------------- 1 | export class Problem { 2 | id: number; 3 | name: string; 4 | desc: string; 5 | difficulty: string; 6 | } 7 | -------------------------------------------------------------------------------- /oj-client/src/app/pipes/search.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { SearchPipe } from './search.pipe'; 2 | 3 | describe('SearchPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new SearchPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /oj-client/src/app/pipes/search.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import { Problem } from '../models/problem.model'; 4 | 5 | @Pipe({ 6 | name: 'search' 7 | }) 8 | export class SearchPipe implements PipeTransform { 9 | 10 | transform(problems: Problem[], term: string): Problem[] { 11 | console.log(problems); 12 | return problems.filter( 13 | problem => problem.name.toLowerCase().includes(term)); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /oj-client/src/app/services/auth-guard.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { AuthGuardService } from './auth-guard.service'; 4 | 5 | describe('AuthGuardService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [AuthGuardService] 9 | }); 10 | }); 11 | 12 | it('should ...', inject([AuthGuardService], (service: AuthGuardService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /oj-client/src/app/services/auth-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Inject } from '@angular/core'; 2 | import { Router, CanActivate } from '@angular/router'; 3 | 4 | @Injectable() 5 | export class AuthGuardService implements CanActivate { 6 | 7 | constructor(@Inject('auth') private auth, private router: Router) { } 8 | 9 | canActivate() { 10 | if (this.auth.authenticated()) { 11 | return true; 12 | } else { 13 | // redirect to home page if not logged in 14 | this.router.navigate(['/problems']); 15 | return false; 16 | } 17 | } 18 | 19 | isAdmin(): boolean { 20 | if (this.auth.authenticated() && this.auth.getProfile().roles.includes('Admin')) { 21 | return true; 22 | } else { 23 | return false; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /oj-client/src/app/services/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { AuthService } from './auth.service'; 4 | 5 | describe('AuthService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [AuthService] 9 | }); 10 | }); 11 | 12 | it('should ...', inject([AuthService], (service: AuthService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /oj-client/src/app/services/auth.service.ts: -------------------------------------------------------------------------------- 1 | // app/auth.service.ts 2 | 3 | import { Injectable } from '@angular/core'; 4 | import { tokenNotExpired } from 'angular2-jwt'; 5 | import { Http, Response, Headers } from '@angular/http'; 6 | import 'rxjs/add/operator/toPromise'; 7 | 8 | // Avoid name not found warnings 9 | declare var Auth0Lock: any; 10 | 11 | @Injectable() 12 | export class AuthService { 13 | // Configure Auth0 14 | clientId = 'oKBLizpdfjbRhIfvcUytmrmji0L1DUBA'; 15 | domain = 'bittiger503codelab.auth0.com'; 16 | lock = new Auth0Lock(this.clientId, this.domain, {}); 17 | 18 | constructor(private http: Http) { 19 | 20 | } 21 | 22 | public login(): Promise { 23 | return new Promise((resolve, reject) => { 24 | // Call the show method to display the widget. 25 | this.lock.show((error: string, profile: Object, id_token: string) => { 26 | if (error) { 27 | reject(error); 28 | } else { 29 | localStorage.setItem('profile', JSON.stringify(profile)); 30 | localStorage.setItem('id_token', id_token); 31 | resolve(profile); 32 | } 33 | }); 34 | }) 35 | } 36 | 37 | public authenticated() { 38 | // Check if there's an unexpired JWT 39 | // This searches for an item in localStorage with key == 'id_token' 40 | return tokenNotExpired(); 41 | } 42 | 43 | public logout() { 44 | // Remove token from localStorage 45 | localStorage.removeItem('id_token'); 46 | localStorage.removeItem('profile'); 47 | } 48 | 49 | public getProfile() { 50 | return JSON.parse(localStorage.getItem('profile')); 51 | } 52 | 53 | public resetPassword(): void { 54 | let profile = this.getProfile(); 55 | let url: string = `https://${this.domain}/dbconnections/change_password`; 56 | let headers = new Headers({ 'content-type': 'application/json' }); 57 | let body = { 58 | client_id: this.clientId, 59 | email: profile.email, 60 | connection: 'Username-Password-Authentication' 61 | } 62 | 63 | this.http.post(url, body, headers) 64 | .toPromise() 65 | .then((res: Response) => { 66 | console.log(res.json()); 67 | }) 68 | .catch(this.handleError); 69 | 70 | 71 | } 72 | private handleError(error: any): Promise { 73 | console.error('Error occurred', error); 74 | return Promise.reject(error.message || error); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /oj-client/src/app/services/collaboration.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { CollaborationService } from './collaboration.service'; 4 | 5 | describe('CollaborationService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [CollaborationService] 9 | }); 10 | }); 11 | 12 | it('should ...', inject([CollaborationService], (service: CollaborationService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /oj-client/src/app/services/collaboration.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { COLORS } from '../../assets/colors'; 4 | 5 | declare var io: any; 6 | declare var ace: any; 7 | 8 | @Injectable() 9 | export class CollaborationService { 10 | 11 | collaborationSocket: any; 12 | clientsInfo: Object = {}; 13 | clientNum: number = 0; 14 | 15 | constructor() { } 16 | 17 | init(editor: any, sessionId: string): void { 18 | this.collaborationSocket = io(window.location.origin, { query: 'sessionId=' + sessionId }); 19 | 20 | this.collaborationSocket.on("change", (delta: string) => { 21 | console.log('collaboration: editor changes by ' + delta); 22 | delta = JSON.parse(delta); 23 | editor.lastAppliedChange = delta; 24 | editor.getSession().getDocument().applyDeltas([delta]); 25 | }); 26 | 27 | this.collaborationSocket.on("cursorMove", (cursor) => { 28 | console.log("cursor move: " + cursor); 29 | let session = editor.getSession(); 30 | cursor = JSON.parse(cursor); 31 | let x = cursor['row']; 32 | let y = cursor['column']; 33 | let changeClientId = cursor['socketId']; 34 | console.log(x + ' ' + y + ' ' + changeClientId); 35 | 36 | if (changeClientId in this.clientsInfo) { 37 | session.removeMarker(this.clientsInfo[changeClientId]['marker']); 38 | } else { 39 | this.clientsInfo[changeClientId] = {}; 40 | 41 | let css = document.createElement("style"); 42 | css.type = "text/css"; 43 | css.innerHTML = ".editor_cursor_" + changeClientId 44 | + " { position:absolute; background:" + COLORS[this.clientNum] + ";" 45 | + " z-index: 100; width:3px !important; }"; 46 | 47 | document.body.appendChild(css); 48 | this.clientNum++; 49 | } 50 | 51 | let Range = ace.require('ace/range').Range; 52 | let newMarker = session.addMarker(new Range(x, y, x, y + 1), 'editor_cursor_' + changeClientId, true); 53 | this.clientsInfo[changeClientId]['marker'] = newMarker; 54 | }); 55 | 56 | // Test 57 | this.collaborationSocket.on("message", (message) => { 58 | console.log("received: " + message); 59 | }) 60 | } 61 | 62 | change(delta: string): void { 63 | this.collaborationSocket.emit("change", delta); 64 | } 65 | 66 | cursorMove(cursor : string): void { 67 | this.collaborationSocket.emit("cursorMove", cursor); 68 | } 69 | 70 | restoreBuffer(): void { 71 | this.collaborationSocket.emit("restoreBuffer"); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /oj-client/src/app/services/data.service.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | 3 | import { TestBed, async, inject } from '@angular/core/testing'; 4 | import { DataService } from './data.service'; 5 | 6 | describe('DataService', () => { 7 | beforeEach(() => { 8 | TestBed.configureTestingModule({ 9 | providers: [DataService] 10 | }); 11 | }); 12 | 13 | it('should ...', inject([DataService], (service: DataService) => { 14 | expect(service).toBeTruthy(); 15 | })); 16 | }); 17 | -------------------------------------------------------------------------------- /oj-client/src/app/services/data.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Problem } from "../models/problem.model"; 3 | import { PROBLEMS } from "../mock-problems"; 4 | import { Http, Response, Headers } from '@angular/http'; 5 | import { BehaviorSubject} from 'rxjs/BehaviorSubject'; 6 | import { Observable } from 'rxjs/Rx'; 7 | import 'rxjs/add/operator/toPromise'; 8 | 9 | 10 | @Injectable() 11 | export class DataService { 12 | 13 | private problemsSource = new BehaviorSubject([]); 14 | 15 | constructor(private http: Http) { } 16 | 17 | getProblems(): Observable { 18 | this.http.get("api/v1/problems") 19 | .toPromise() 20 | .then((res: Response) => { 21 | this.problemsSource.next(res.json()); 22 | }) 23 | .catch(this.handleError); 24 | 25 | return this.problemsSource.asObservable(); 26 | } 27 | 28 | getProblem(id: number): Promise { 29 | return this.http.get(`api/v1/problems/${id}`) 30 | .toPromise() 31 | .then((res: Response) => res.json()) 32 | .catch(this.handleError); 33 | } 34 | 35 | addProblem(problem: Problem): Promise { 36 | let headers = new Headers({ 'content-type': 'application/json' }); 37 | return this.http.post('/api/v1/problems', problem, headers) 38 | .toPromise() 39 | .then((res: Response) => { 40 | this.getProblems(); 41 | return res.json(); 42 | }) 43 | .catch(this.handleError); 44 | } 45 | 46 | buildAndRun(data): Promise { 47 | let headers = new Headers({'content-type': 'application/json'}); 48 | return this.http.post('/api/v1/build_and_run', data, headers) 49 | .toPromise() 50 | .then((res: Response) => { 51 | console.log(res); 52 | return res.json(); 53 | }) 54 | .catch(this.handleError); 55 | } 56 | 57 | // error hanlder 58 | private handleError(error: any): Promise { 59 | console.error('An error occurred', error); // for demo purposes only 60 | return Promise.reject(error.body || error); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /oj-client/src/app/services/input.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { InputService } from './input.service'; 4 | 5 | describe('InputService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [InputService] 9 | }); 10 | }); 11 | 12 | it('should ...', inject([InputService], (service: InputService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /oj-client/src/app/services/input.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import {BehaviorSubject } from 'rxjs/BehaviorSubject'; 3 | import {Observable} from 'rxjs/Rx'; 4 | 5 | @Injectable() 6 | export class InputService { 7 | private inputSubject$ = new BehaviorSubject(''); 8 | 9 | 10 | constructor() { } 11 | 12 | changeInput(term) { 13 | this.inputSubject$.next(term); 14 | } 15 | 16 | getInput(): Observable { 17 | return this.inputSubject$.asObservable(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /oj-client/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/oj-client/src/assets/.gitkeep -------------------------------------------------------------------------------- /oj-client/src/assets/colors.ts: -------------------------------------------------------------------------------- 1 | export const COLORS: [string] = [ 2 | "#0000ff", 3 | "#a52a2a", 4 | "#00ffff", 5 | "#00008b", 6 | "#008b8b", 7 | "#a9a9a9", 8 | "#006400", 9 | "#bdb76b", 10 | "#8b008b", 11 | "#556b2f", 12 | "#ff8c00", 13 | "#9932cc", 14 | "#8b0000", 15 | "#e9967a", 16 | "#9400d3", 17 | "#ff00ff", 18 | "#ffd700", 19 | "#008000", 20 | "#4b0082", 21 | "#f0e68c", 22 | "#add8e6", 23 | "#e0ffff", 24 | "#90ee90", 25 | "#d3d3d3", 26 | "#ffb6c1", 27 | "#ffffe0", 28 | "#00ff00", 29 | "#ff00ff", 30 | "#800000", 31 | "#000080", 32 | "#808000", 33 | "#ffa500", 34 | "#ffc0cb", 35 | "#800080", 36 | "#800080", 37 | "#ff0000", 38 | "#c0c0c0", 39 | "#ffffff", 40 | "#ffff00" 41 | ]; 42 | -------------------------------------------------------------------------------- /oj-client/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /oj-client/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /oj-client/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarac/angular-online-judge/1037d15d0e64e33ee52fe4323d59ba42b9dc77ed/oj-client/src/favicon.ico -------------------------------------------------------------------------------- /oj-client/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OjClient 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Loading... 15 | 16 | 17 | -------------------------------------------------------------------------------- /oj-client/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | import { enableProdMode } from '@angular/core'; 3 | import { environment } from './environments/environment'; 4 | import { AppModule } from './app/app.module'; 5 | 6 | if (environment.production) { 7 | enableProdMode(); 8 | } 9 | 10 | platformBrowserDynamic().bootstrapModule(AppModule); 11 | -------------------------------------------------------------------------------- /oj-client/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | // This file includes polyfills needed by Angular and is loaded before the app. 2 | // You can add your own extra polyfills to this file. 3 | import 'core-js/es6/symbol'; 4 | import 'core-js/es6/object'; 5 | import 'core-js/es6/function'; 6 | import 'core-js/es6/parse-int'; 7 | import 'core-js/es6/parse-float'; 8 | import 'core-js/es6/number'; 9 | import 'core-js/es6/math'; 10 | import 'core-js/es6/string'; 11 | import 'core-js/es6/date'; 12 | import 'core-js/es6/array'; 13 | import 'core-js/es6/regexp'; 14 | import 'core-js/es6/map'; 15 | import 'core-js/es6/set'; 16 | import 'core-js/es6/reflect'; 17 | 18 | import 'core-js/es7/reflect'; 19 | import 'zone.js/dist/zone'; 20 | 21 | // If you need to support the browsers/features below, uncomment the import 22 | // and run `npm install import-name-here'; 23 | // Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 24 | 25 | // Needed for: IE9 26 | // import 'classlist.js'; 27 | 28 | // Animations 29 | // Needed for: All but Chrome and Firefox, Not supported in IE9 30 | // import 'web-animations-js'; 31 | 32 | // Date, currency, decimal and percent pipes 33 | // Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 34 | // import 'intl'; 35 | 36 | // NgClass on SVG elements 37 | // Needed for: IE10, IE11 38 | // import 'classlist.js'; 39 | -------------------------------------------------------------------------------- /oj-client/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /oj-client/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare var __karma__: any; 17 | declare var require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /oj-client/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "", 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "lib": ["es6", "dom"], 8 | "mapRoot": "./", 9 | "module": "es6", 10 | "moduleResolution": "node", 11 | "outDir": "../dist/out-tsc", 12 | "sourceMap": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "../node_modules/@types" 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /oj-client/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "callable-types": true, 7 | "class-name": true, 8 | "comment-format": [ 9 | true, 10 | "check-space" 11 | ], 12 | "curly": true, 13 | "eofline": true, 14 | "forin": true, 15 | "import-blacklist": [true, "rxjs"], 16 | "import-spacing": true, 17 | "indent": [ 18 | true, 19 | "spaces" 20 | ], 21 | "interface-over-type-literal": true, 22 | "label-position": true, 23 | "max-line-length": [ 24 | true, 25 | 140 26 | ], 27 | "member-access": false, 28 | "member-ordering": [ 29 | true, 30 | "static-before-instance", 31 | "variables-before-functions" 32 | ], 33 | "no-arg": true, 34 | "no-bitwise": true, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-construct": true, 44 | "no-debugger": true, 45 | "no-duplicate-variable": true, 46 | "no-empty": false, 47 | "no-empty-interface": true, 48 | "no-eval": true, 49 | "no-inferrable-types": true, 50 | "no-shadowed-variable": true, 51 | "no-string-literal": false, 52 | "no-string-throw": true, 53 | "no-switch-case-fall-through": true, 54 | "no-trailing-whitespace": true, 55 | "no-unused-expression": true, 56 | "no-use-before-declare": true, 57 | "no-var-keyword": true, 58 | "object-literal-sort-keys": false, 59 | "one-line": [ 60 | true, 61 | "check-open-brace", 62 | "check-catch", 63 | "check-else", 64 | "check-whitespace" 65 | ], 66 | "prefer-const": true, 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "radix": true, 72 | "semicolon": [ 73 | "always" 74 | ], 75 | "triple-equals": [ 76 | true, 77 | "allow-null-check" 78 | ], 79 | "typedef-whitespace": [ 80 | true, 81 | { 82 | "call-signature": "nospace", 83 | "index-signature": "nospace", 84 | "parameter": "nospace", 85 | "property-declaration": "nospace", 86 | "variable-declaration": "nospace" 87 | } 88 | ], 89 | "typeof-compare": true, 90 | "unified-signatures": true, 91 | "variable-name": false, 92 | "whitespace": [ 93 | true, 94 | "check-branch", 95 | "check-decl", 96 | "check-operator", 97 | "check-separator", 98 | "check-type" 99 | ], 100 | 101 | "directive-selector": [true, "attribute", "app", "camelCase"], 102 | "component-selector": [true, "element", "app", "kebab-case"], 103 | "use-input-property-decorator": true, 104 | "use-output-property-decorator": true, 105 | "use-host-property-decorator": true, 106 | "no-input-rename": true, 107 | "no-output-rename": true, 108 | "use-life-cycle-interface": true, 109 | "use-pipe-transform-interface": true, 110 | "component-class-suffix": true, 111 | "directive-class-suffix": true, 112 | "no-access-missing-member": true, 113 | "templates-use-public": true, 114 | "invoke-injectable": true 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /oj-server/models/problemModel.js: -------------------------------------------------------------------------------- 1 | var mongoose = require("mongoose"); 2 | var ProblemSchema = mongoose.Schema({ 3 | id: Number, 4 | name: String, 5 | desc: String, 6 | difficulty: String 7 | }); 8 | var problemModel = mongoose.model("ProblemModel", ProblemSchema); 9 | 10 | module.exports = problemModel; 11 | -------------------------------------------------------------------------------- /oj-server/modules/redisClient.js: -------------------------------------------------------------------------------- 1 | var redis = require('redis'); 2 | var client = redis.createClient(); 3 | 4 | function set(key, value, callback) { 5 | client.set(key, value, function(err, res) { 6 | if (err) { 7 | console.log(err); 8 | return; 9 | } 10 | callback(res); 11 | }); 12 | } 13 | 14 | function get(key, callback) { 15 | client.get(key, function(err, res) { 16 | if (err) { 17 | console.log(err); 18 | return; 19 | } 20 | callback(res); 21 | }); 22 | } 23 | 24 | function expire(key, timeInSeconds) { 25 | client.expire(key, timeInSeconds); 26 | } 27 | 28 | function quit() { 29 | client.quit(); 30 | } 31 | 32 | module.exports = { 33 | get: get, 34 | set: set, 35 | expire: expire, 36 | quit: quit, 37 | redisPrint: redis.print 38 | } 39 | -------------------------------------------------------------------------------- /oj-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oj-server", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.17.0", 13 | "express": "^4.15.0", 14 | "mongoose": "^4.8.5", 15 | "node-rest-client": "^3.0.7", 16 | "redis": "^2.7.1", 17 | "socket.io": "^1.7.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /oj-server/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var path = require('path'); 4 | 5 | router.get('/', function(req, res) { 6 | // send index.html to start client side 7 | res.sendFile("index.html", { root: path.join(__dirname, '../../public/') }); 8 | }); 9 | 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /oj-server/routes/rest.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | var problemService = require("../services/problemService"); 4 | var bodyParser = require("body-parser"); 5 | var jsonParser = bodyParser.json(); 6 | 7 | var node_rest_client = require('node-rest-client').Client; 8 | var rest_client = new node_rest_client(); 9 | 10 | EXECUTOR_SERVER_URL = 'http://localhost:5000/build_and_run' 11 | 12 | rest_client.registerMethod('build_and_run', EXECUTOR_SERVER_URL, 'POST'); 13 | 14 | 15 | router.get("/problems", function (req, res) { 16 | problemService.getProblems() 17 | .then(problems => res.json(problems)); 18 | }); 19 | 20 | router.get("/problems/:id", function (req, res) { 21 | var id = req.params.id; 22 | problemService.getProblem(+id) 23 | .then(problem => res.json(problem)); 24 | }); 25 | 26 | router.post("/problems", jsonParser, function (req, res) { 27 | problemService.addProblem(req.body) 28 | .then(function (problem) { 29 | res.json(problem); 30 | }, function (error) { 31 | res.status(400).send("Problem name already exists!"); 32 | }); 33 | }); 34 | 35 | router.post('/build_and_run', jsonParser, function(req, res) { 36 | const userCode = req.body.user_code; 37 | const lang = req.body.lang; 38 | console.log(lang + ': ' + userCode); 39 | // Send build and run request to executor. 40 | rest_client.methods.build_and_run( 41 | {data: { code: userCode, lang: lang }, 42 | headers: { "Content-Type": "application/json" } 43 | }, (data, response) => { 44 | console.log('Recieved response from execution server: '); 45 | console.log(response); 46 | // Generate a human readable response displayed in output textarea. 47 | const text =`Build output: ${data['build']} 48 | Execute output: ${data['run']}`; 49 | 50 | data['text'] = text; 51 | res.json(data); 52 | }); 53 | }); 54 | 55 | module.exports = router; 56 | -------------------------------------------------------------------------------- /oj-server/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | var app = express() 3 | var restRouter = require("./routes/rest"); 4 | var indexRouter = require("./routes/index"); 5 | var mongoose = require("mongoose"); 6 | var path = require("path"); 7 | var http = require('http'); 8 | 9 | var socket_io = require('socket.io'); 10 | var io = socket_io(); 11 | var socketService = require('./services/SocketService.js')(io); 12 | 13 | mongoose.connect('mongodb://user:user@ds145039.mlab.com:45039/collaborative_oj'); 14 | 15 | app.use(express.static(path.join(__dirname, '../public'))); 16 | 17 | app.use('/', indexRouter); 18 | app.use("/api/v1", restRouter); 19 | 20 | app.use(function(req, res) { 21 | // send index.html to start client side 22 | res.sendFile("index.html", { root: path.join(__dirname, '../public/') }); 23 | }); 24 | 25 | 26 | var server = http.createServer(app); 27 | io.attach(server); 28 | server.listen(3000); 29 | 30 | server.on('error', onError); 31 | server.on('listening', onListening); 32 | 33 | function onError(error){ 34 | throw error; 35 | } 36 | 37 | function onListening() { 38 | var addr = server.address(); 39 | var bind = typeof addr == 'string' 40 | ? 'pipe ' + addr 41 | : 'port ' + addr.port; 42 | console.log('Listening on ' + bind); 43 | } 44 | -------------------------------------------------------------------------------- /oj-server/services/SocketService.js: -------------------------------------------------------------------------------- 1 | var redisClient = require('../modules/redisClient'); 2 | const TIMEOUT_IN_SECONDS = 3600; 3 | 4 | module.exports = function(io) { 5 | // collaboration sessions 6 | var collaborations = []; 7 | // map from socketId to sessionId 8 | var socketIdToSessionId = []; 9 | 10 | var sessionPath = "/temp_sessions"; 11 | 12 | io.on('connection', socket => { 13 | let sessionId = socket.handshake.query['sessionId']; 14 | 15 | socketIdToSessionId[socket.id] = sessionId; 16 | // add socket.id to corresponding collaboration session participants 17 | if (sessionId in collaborations) { 18 | collaborations[sessionId]['participants'].push(socket.id); 19 | } else { 20 | redisClient.get(sessionPath + '/' + sessionId, function(data) { 21 | if (data) { 22 | console.log("session terminiated previsouly; pulling back from Redis."); 23 | collaborations[sessionId] = { 24 | 'cachedChangeEvents': JSON.parse(data), 25 | 'participants': [] 26 | }; 27 | } else { 28 | console.log("creating new session"); 29 | collaborations[sessionId] = { 30 | 'cachedChangeEvents': [], 31 | 'participants': [] 32 | }; 33 | } 34 | collaborations[sessionId]['participants'].push(socket.id); 35 | }); 36 | } 37 | 38 | 39 | // socket event listeners 40 | socket.on('change', delta => { 41 | console.log( "change " + socketIdToSessionId[socket.id] + " " + delta ) ; 42 | let sessionId = socketIdToSessionId[socket.id]; 43 | 44 | if (sessionId in collaborations) { 45 | collaborations[sessionId]['cachedChangeEvents'].push(["change", delta, Date.now()]); 46 | } 47 | 48 | forwardEvents(socket.id, 'change', delta); 49 | }); 50 | 51 | // handle cursorMove events 52 | socket.on('cursorMove', cursor => { 53 | console.log( "cursorMove " + socketIdToSessionId[socket.id] + " " + cursor ) ; 54 | cursor = JSON.parse(cursor); 55 | cursor['socketId'] = socket.id; 56 | 57 | forwardEvents(socket.id, 'cursorMove', JSON.stringify(cursor)); 58 | }); 59 | 60 | socket.on('restoreBuffer', () => { 61 | let sessionId = socketIdToSessionId[socket.id]; 62 | console.log('restoring buffer for session: ' + sessionId + ', socket: ' + socket.id); 63 | if (sessionId in collaborations) { 64 | let changeEvents = collaborations[sessionId]['cachedChangeEvents']; 65 | for (let i = 0; i < changeEvents.length; i++) { 66 | socket.emit(changeEvents[i][0], changeEvents[i][1]); 67 | } 68 | } 69 | }); 70 | 71 | socket.on('disconnect', function() { 72 | let sessionId = socketIdToSessionId[socket.id]; 73 | console.log('socket ' + socket.id + 'disconnected.'); 74 | 75 | if (sessionId in collaborations) { 76 | let participants = collaborations[sessionId]['participants']; 77 | let index = participants.indexOf(socket.id); 78 | if (index >= 0) { 79 | participants.splice(index, 1); 80 | if (participants.length == 0) { 81 | console.log("last participant left. Storing in Redis."); 82 | let key = sessionPath + "/" + sessionId; 83 | let value = JSON.stringify(collaborations[sessionId]['cachedChangeEvents']); 84 | redisClient.set(key, value, redisClient.redisPrint); 85 | redisClient.expire(key, TIMEOUT_IN_SECONDS); 86 | delete collaborations[sessionId]; 87 | } 88 | } 89 | } 90 | }); 91 | 92 | function forwardEvents(socketId, eventName, dataString) { 93 | let sessionId = socketIdToSessionId[socketId]; 94 | 95 | if (sessionId in collaborations) { 96 | let participants = collaborations[sessionId]['participants']; 97 | for (let i = 0; i < participants.length; i++) { 98 | if (socket.id != participants[i]) { 99 | io.to(participants[i]).emit(eventName, dataString); 100 | } 101 | } 102 | } else { 103 | console.log("WARNING: could not tie socket_id to any collaboration"); 104 | } 105 | } 106 | }); 107 | } 108 | -------------------------------------------------------------------------------- /oj-server/services/problemService.js: -------------------------------------------------------------------------------- 1 | var ProblemModel = require("../models/problemModel"); 2 | 3 | var getProblems = function () { 4 | return new Promise((resolve,reject) => { 5 | ProblemModel.find({}, function (err, problems) { 6 | if (err) { 7 | reject(err); 8 | } else { 9 | resolve(problems); 10 | } 11 | }); 12 | }); 13 | } 14 | 15 | var getProblem = function (id) { 16 | return new Promise((resolve,reject) => { 17 | ProblemModel.findOne({ id: id }, function (err, problem) { 18 | if (err) { 19 | reject(err); 20 | } else { 21 | resolve(problem); 22 | } 23 | }); 24 | }); 25 | } 26 | 27 | var addProblem = function (newProblem) { 28 | return new Promise((resolve,reject) => { 29 | ProblemModel.findOne({ name: newProblem.name }, function (err, problem) { 30 | if (problem) { 31 | reject("Problem name already exists"); 32 | } else { 33 | ProblemModel.count({}, function (err, num) { 34 | newProblem.id = num + 1; 35 | var mongoProblem = new ProblemModel(newProblem); 36 | mongoProblem.save(); 37 | resolve(newProblem); 38 | }); 39 | } 40 | }); 41 | }) 42 | } 43 | 44 | module.exports = { 45 | getProblems: getProblems, 46 | getProblem: getProblem, 47 | addProblem: addProblem 48 | } 49 | --------------------------------------------------------------------------------