OLD | NEW |
1 package main | 1 package main |
2 | 2 |
3 import ( | 3 import ( |
4 "bytes" | 4 "bytes" |
5 "crypto/md5" | 5 "crypto/md5" |
6 "database/sql" | 6 "database/sql" |
7 "encoding/base64" | 7 "encoding/base64" |
8 "encoding/json" | 8 "encoding/json" |
9 "flag" | 9 "flag" |
10 "fmt" | 10 "fmt" |
11 _ "github.com/go-sql-driver/mysql" | 11 _ "github.com/go-sql-driver/mysql" |
12 _ "github.com/mattn/go-sqlite3" | 12 _ "github.com/mattn/go-sqlite3" |
13 htemplate "html/template" | 13 htemplate "html/template" |
14 "io/ioutil" | 14 "io/ioutil" |
15 "log" | 15 "log" |
| 16 "math/rand" |
16 "net/http" | 17 "net/http" |
17 "os" | 18 "os" |
18 "os/exec" | 19 "os/exec" |
19 "path/filepath" | 20 "path/filepath" |
20 "regexp" | 21 "regexp" |
21 "strings" | 22 "strings" |
22 "text/template" | 23 "text/template" |
23 "time" | 24 "time" |
24 ) | 25 ) |
25 | 26 |
(...skipping 12 matching lines...) Expand all Loading... |
38 MAX_TRY_SIZE = 64000 | 39 MAX_TRY_SIZE = 64000 |
39 ) | 40 ) |
40 | 41 |
41 var ( | 42 var ( |
42 // codeTemplate is the cpp code template the user's code is copied into. | 43 // codeTemplate is the cpp code template the user's code is copied into. |
43 codeTemplate *template.Template = nil | 44 codeTemplate *template.Template = nil |
44 | 45 |
45 // indexTemplate is the main index.html page we serve. | 46 // indexTemplate is the main index.html page we serve. |
46 indexTemplate *htemplate.Template = nil | 47 indexTemplate *htemplate.Template = nil |
47 | 48 |
48 » // recentTemplate is a list of recent images. | 49 » // recentTemplate is a list of recent images. |
49 recentTemplate *htemplate.Template = nil | 50 recentTemplate *htemplate.Template = nil |
50 | 51 |
| 52 // workspaceTemplate is the page for workspaces, a series of webtrys. |
| 53 workspaceTemplate *htemplate.Template = nil |
| 54 |
51 // db is the database, nil if we don't have an SQL database to store dat
a into. | 55 // db is the database, nil if we don't have an SQL database to store dat
a into. |
52 db *sql.DB = nil | 56 db *sql.DB = nil |
53 | 57 |
54 // directLink is the regex that matches URLs paths that are direct links
. | 58 // directLink is the regex that matches URLs paths that are direct links
. |
55 directLink = regexp.MustCompile("^/c/([a-f0-9]+)$") | 59 directLink = regexp.MustCompile("^/c/([a-f0-9]+)$") |
56 | 60 |
57 // imageLink is the regex that matches URLs paths that are direct links
to PNGs. | 61 // imageLink is the regex that matches URLs paths that are direct links
to PNGs. |
58 imageLink = regexp.MustCompile("^/i/([a-f0-9]+.png)$") | 62 imageLink = regexp.MustCompile("^/i/([a-f0-9]+.png)$") |
| 63 |
| 64 // workspaceLink is the regex that matches URLs paths for workspaces. |
| 65 workspaceLink = regexp.MustCompile("^/w/([a-z0-9-]+)$") |
| 66 |
| 67 // workspaceNameAdj is a list of adjectives for building workspace names
. |
| 68 workspaceNameAdj = []string{ |
| 69 "autumn", "hidden", "bitter", "misty", "silent", "empty", "dry",
"dark", |
| 70 "summer", "icy", "delicate", "quiet", "white", "cool", "spring",
"winter", |
| 71 "patient", "twilight", "dawn", "crimson", "wispy", "weathered",
"blue", |
| 72 "billowing", "broken", "cold", "damp", "falling", "frosty", "gre
en", |
| 73 "long", "late", "lingering", "bold", "little", "morning", "muddy
", "old", |
| 74 "red", "rough", "still", "small", "sparkling", "throbbing", "shy
", |
| 75 "wandering", "withered", "wild", "black", "young", "holy", "soli
tary", |
| 76 "fragrant", "aged", "snowy", "proud", "floral", "restless", "div
ine", |
| 77 "polished", "ancient", "purple", "lively", "nameless", |
| 78 } |
| 79 |
| 80 // workspaceNameNoun is a list of nouns for building workspace names. |
| 81 workspaceNameNoun = []string{ |
| 82 "waterfall", "river", "breeze", "moon", "rain", "wind", "sea", "
morning", |
| 83 "snow", "lake", "sunset", "pine", "shadow", "leaf", "dawn", "gli
tter", |
| 84 "forest", "hill", "cloud", "meadow", "sun", "glade", "bird", "br
ook", |
| 85 "butterfly", "bush", "dew", "dust", "field", "fire", "flower", "
firefly", |
| 86 "feather", "grass", "haze", "mountain", "night", "pond", "darkne
ss", |
| 87 "snowflake", "silence", "sound", "sky", "shape", "surf", "thunde
r", |
| 88 "violet", "water", "wildflower", "wave", "water", "resonance", "
sun", |
| 89 "wood", "dream", "cherry", "tree", "fog", "frost", "voice", "pap
er", |
| 90 "frog", "smoke", "star", |
| 91 } |
59 ) | 92 ) |
60 | 93 |
61 // flags | 94 // flags |
62 var ( | 95 var ( |
63 useChroot = flag.Bool("use_chroot", false, "Run the compiled code in the
schroot jail.") | 96 useChroot = flag.Bool("use_chroot", false, "Run the compiled code in the
schroot jail.") |
64 port = flag.String("port", ":8000", "HTTP service address (e.g., ':
8000')") | 97 port = flag.String("port", ":8000", "HTTP service address (e.g., ':
8000')") |
65 ) | 98 ) |
66 | 99 |
67 // lineNumbers adds #line numbering to the user's code. | 100 // lineNumbers adds #line numbering to the user's code. |
68 func LineNumbers(c string) string { | 101 func LineNumbers(c string) string { |
(...skipping 14 matching lines...) Expand all Loading... |
83 if err != nil { | 116 if err != nil { |
84 log.Fatal(err) | 117 log.Fatal(err) |
85 } | 118 } |
86 os.Chdir(cwd) | 119 os.Chdir(cwd) |
87 | 120 |
88 codeTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/te
mplate.cpp")) | 121 codeTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/te
mplate.cpp")) |
89 if err != nil { | 122 if err != nil { |
90 panic(err) | 123 panic(err) |
91 } | 124 } |
92 // Convert index.html into a template, which is expanded with the code. | 125 // Convert index.html into a template, which is expanded with the code. |
93 » indexTemplate, err = htemplate.ParseFiles(filepath.Join(cwd, "templates/
index.html")) | 126 » indexTemplate, err = htemplate.ParseFiles( |
| 127 » » filepath.Join(cwd, "templates/index.html"), |
| 128 » » filepath.Join(cwd, "templates/titlebar.html"), |
| 129 » ) |
94 if err != nil { | 130 if err != nil { |
95 panic(err) | 131 panic(err) |
96 } | 132 } |
97 | 133 » recentTemplate, err = htemplate.ParseFiles( |
98 » recentTemplate, err = htemplate.ParseFiles(filepath.Join(cwd, "templates
/recent.html")) | 134 » » filepath.Join(cwd, "templates/recent.html"), |
| 135 » » filepath.Join(cwd, "templates/titlebar.html"), |
| 136 » ) |
99 if err != nil { | 137 if err != nil { |
100 panic(err) | 138 panic(err) |
101 } | 139 } |
| 140 workspaceTemplate, err = htemplate.ParseFiles( |
| 141 filepath.Join(cwd, "templates/workspace.html"), |
| 142 filepath.Join(cwd, "templates/titlebar.html"), |
| 143 ) |
| 144 if err != nil { |
| 145 panic(err) |
| 146 } |
102 | 147 |
103 // Connect to MySQL server. First, get the password from the metadata se
rver. | 148 // Connect to MySQL server. First, get the password from the metadata se
rver. |
104 // See https://developers.google.com/compute/docs/metadata#custom. | 149 // See https://developers.google.com/compute/docs/metadata#custom. |
105 req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/i
nstance/attributes/password", nil) | 150 req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/i
nstance/attributes/password", nil) |
106 if err != nil { | 151 if err != nil { |
107 panic(err) | 152 panic(err) |
108 } | 153 } |
109 client := http.Client{} | 154 client := http.Client{} |
110 req.Header.Add("X-Google-Metadata-Request", "True") | 155 req.Header.Add("X-Google-Metadata-Request", "True") |
111 if resp, err := client.Do(req); err == nil { | 156 if resp, err := client.Do(req); err == nil { |
112 password, err := ioutil.ReadAll(resp.Body) | 157 password, err := ioutil.ReadAll(resp.Body) |
113 if err != nil { | 158 if err != nil { |
114 log.Printf("ERROR: Failed to read password from metadata
server: %q\n", err) | 159 log.Printf("ERROR: Failed to read password from metadata
server: %q\n", err) |
115 panic(err) | 160 panic(err) |
116 } | 161 } |
117 // The IP address of the database is found here: | 162 // The IP address of the database is found here: |
118 // https://console.developers.google.com/project/31977622648/
sql/instances/webtry/overview | 163 // https://console.developers.google.com/project/31977622648/
sql/instances/webtry/overview |
119 // And 3306 is the default port for MySQL. | 164 // And 3306 is the default port for MySQL. |
120 db, err = sql.Open("mysql", fmt.Sprintf("webtry:%s@tcp(173.194.8
3.52:3306)/webtry?parseTime=true", password)) | 165 db, err = sql.Open("mysql", fmt.Sprintf("webtry:%s@tcp(173.194.8
3.52:3306)/webtry?parseTime=true", password)) |
121 if err != nil { | 166 if err != nil { |
122 log.Printf("ERROR: Failed to open connection to SQL serv
er: %q\n", err) | 167 log.Printf("ERROR: Failed to open connection to SQL serv
er: %q\n", err) |
123 panic(err) | 168 panic(err) |
124 } | 169 } |
125 } else { | 170 } else { |
| 171 log.Printf("INFO: Failed to find metadata, unable to connect to
MySQL server (Expected when running locally): %q\n", err) |
126 // Fallback to sqlite for local use. | 172 // Fallback to sqlite for local use. |
127 db, err = sql.Open("sqlite3", "./webtry.db") | 173 db, err = sql.Open("sqlite3", "./webtry.db") |
128 if err != nil { | 174 if err != nil { |
129 log.Printf("ERROR: Failed to open: %q\n", err) | 175 log.Printf("ERROR: Failed to open: %q\n", err) |
130 panic(err) | 176 panic(err) |
131 } | 177 } |
132 sql := `CREATE TABLE webtry ( | 178 sql := `CREATE TABLE webtry ( |
133 code TEXT DEFAULT '' NOT NULL, | 179 code TEXT DEFAULT '' NOT NULL, |
134 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, | 180 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, |
135 hash CHAR(64) DEFAULT '' NOT NULL, | 181 hash CHAR(64) DEFAULT '' NOT NULL, |
136 PRIMARY KEY(hash) | 182 PRIMARY KEY(hash) |
137 )` | 183 )` |
138 » » db.Exec(sql) | 184 » » _, err = db.Exec(sql) |
139 » » log.Printf("INFO: Failed to find metadata, unable to connect to
MySQL server (Expected when running locally): %q\n", err) | 185 » » log.Printf("Info: status creating sqlite table for webtry: %q\n"
, err) |
| 186 » » sql = `CREATE TABLE workspace ( |
| 187 name CHAR(64) DEFAULT '' NOT NULL, |
| 188 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, |
| 189 PRIMARY KEY(name) |
| 190 )` |
| 191 » » _, err = db.Exec(sql) |
| 192 » » log.Printf("Info: status creating sqlite table for workspace: %q
\n", err) |
| 193 » » sql = `CREATE TABLE workspacetry ( |
| 194 name CHAR(64) DEFAULT '' NOT NULL, |
| 195 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, |
| 196 hash CHAR(64) DEFAULT '' NOT NULL, |
| 197 hidden INTEGER DEFAULT 0 NOT NULL, |
| 198 |
| 199 FOREIGN KEY (name) REFERENCES workspace(name) |
| 200 )` |
| 201 » » _, err = db.Exec(sql) |
| 202 » » log.Printf("Info: status creating sqlite table for workspace try
: %q\n", err) |
140 } | 203 } |
141 } | 204 } |
142 | 205 |
143 // userCode is used in template expansion. | 206 // userCode is used in template expansion. |
144 type userCode struct { | 207 type userCode struct { |
145 UserCode string | 208 UserCode string |
146 } | 209 } |
147 | 210 |
148 // expandToFile expands the template and writes the result to the file. | 211 // expandToFile expands the template and writes the result to the file. |
149 func expandToFile(filename string, code string, t *template.Template) error { | 212 func expandToFile(filename string, code string, t *template.Template) error { |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
224 } | 287 } |
225 log.Printf("Error: %s\n%s", message, err.Error()) | 288 log.Printf("Error: %s\n%s", message, err.Error()) |
226 resp, err := json.Marshal(m) | 289 resp, err := json.Marshal(m) |
227 if err != nil { | 290 if err != nil { |
228 http.Error(w, "Failed to serialize a response", 500) | 291 http.Error(w, "Failed to serialize a response", 500) |
229 return | 292 return |
230 } | 293 } |
231 w.Write(resp) | 294 w.Write(resp) |
232 } | 295 } |
233 | 296 |
234 func writeToDatabase(hash string, code string) { | 297 func writeToDatabase(hash string, code string, workspaceName string) { |
235 if db == nil { | 298 if db == nil { |
236 return | 299 return |
237 } | 300 } |
238 if _, err := db.Exec("INSERT INTO webtry (code, hash) VALUES(?, ?)", cod
e, hash); err != nil { | 301 if _, err := db.Exec("INSERT INTO webtry (code, hash) VALUES(?, ?)", cod
e, hash); err != nil { |
239 log.Printf("ERROR: Failed to insert code into database: %q\n", e
rr) | 302 log.Printf("ERROR: Failed to insert code into database: %q\n", e
rr) |
240 } | 303 } |
| 304 if workspaceName != "" { |
| 305 if _, err := db.Exec("INSERT INTO workspacetry (name, hash) VALU
ES(?, ?)", workspaceName, hash); err != nil { |
| 306 log.Printf("ERROR: Failed to insert into workspacetry ta
ble: %q\n", err) |
| 307 } |
| 308 } |
241 } | 309 } |
242 | 310 |
243 func cssHandler(w http.ResponseWriter, r *http.Request) { | 311 func cssHandler(w http.ResponseWriter, r *http.Request) { |
244 http.ServeFile(w, r, "css/webtry.css") | 312 http.ServeFile(w, r, "css/webtry.css") |
245 } | 313 } |
246 | 314 |
| 315 func jsHandler(w http.ResponseWriter, r *http.Request) { |
| 316 http.ServeFile(w, r, "js/run.js") |
| 317 } |
| 318 |
247 // imageHandler serves up the PNG of a specific try. | 319 // imageHandler serves up the PNG of a specific try. |
248 func imageHandler(w http.ResponseWriter, r *http.Request) { | 320 func imageHandler(w http.ResponseWriter, r *http.Request) { |
249 log.Printf("Image Handler: %q\n", r.URL.Path) | 321 log.Printf("Image Handler: %q\n", r.URL.Path) |
250 if r.Method != "GET" { | 322 if r.Method != "GET" { |
251 http.NotFound(w, r) | 323 http.NotFound(w, r) |
252 return | 324 return |
253 } | 325 } |
254 match := imageLink.FindStringSubmatch(r.URL.Path) | 326 match := imageLink.FindStringSubmatch(r.URL.Path) |
255 if len(match) != 2 { | 327 if len(match) != 2 { |
256 http.NotFound(w, r) | 328 http.NotFound(w, r) |
(...skipping 30 matching lines...) Expand all Loading... |
287 log.Printf("Error: failed to fetch from database: %q", e
rr) | 359 log.Printf("Error: failed to fetch from database: %q", e
rr) |
288 continue | 360 continue |
289 } | 361 } |
290 recent = append(recent, Try{Hash: hash, CreateTS: create_ts.Form
at("2006-02-01")}) | 362 recent = append(recent, Try{Hash: hash, CreateTS: create_ts.Form
at("2006-02-01")}) |
291 } | 363 } |
292 if err := recentTemplate.Execute(w, Recent{Tries: recent}); err != nil { | 364 if err := recentTemplate.Execute(w, Recent{Tries: recent}); err != nil { |
293 log.Printf("ERROR: Failed to expand template: %q\n", err) | 365 log.Printf("ERROR: Failed to expand template: %q\n", err) |
294 } | 366 } |
295 } | 367 } |
296 | 368 |
| 369 type Workspace struct { |
| 370 Name string |
| 371 Code string |
| 372 Tries []Try |
| 373 } |
| 374 |
| 375 // newWorkspace generates a new random workspace name and stores it in the datab
ase. |
| 376 func newWorkspace() (string, error) { |
| 377 for i := 0; i < 10; i++ { |
| 378 adj := workspaceNameAdj[rand.Intn(len(workspaceNameAdj))] |
| 379 noun := workspaceNameNoun[rand.Intn(len(workspaceNameNoun))] |
| 380 suffix := rand.Intn(1000) |
| 381 name := fmt.Sprintf("%s-%s-%d", adj, noun, suffix) |
| 382 if _, err := db.Exec("INSERT INTO workspace (name) VALUES(?)", n
ame); err == nil { |
| 383 return name, nil |
| 384 } else { |
| 385 log.Printf("ERROR: Failed to insert workspace into datab
ase: %q\n", err) |
| 386 } |
| 387 } |
| 388 return "", fmt.Errorf("Failed to create a new workspace") |
| 389 } |
| 390 |
| 391 // getCode returns the code for a given hash, or the empty string if not found. |
| 392 func getCode(hash string) string { |
| 393 code := "" |
| 394 if err := db.QueryRow("SELECT code FROM webtry WHERE hash=?", hash).Scan
(&code); err != nil { |
| 395 log.Printf("ERROR: Code for hash is missing: %q\n", err) |
| 396 } |
| 397 return code |
| 398 } |
| 399 |
| 400 func workspaceHandler(w http.ResponseWriter, r *http.Request) { |
| 401 log.Printf("Workspace Handler: %q\n", r.URL.Path) |
| 402 if r.Method == "GET" { |
| 403 tries := []Try{} |
| 404 match := workspaceLink.FindStringSubmatch(r.URL.Path) |
| 405 name := "" |
| 406 if len(match) == 2 { |
| 407 name = match[1] |
| 408 rows, err := db.Query("SELECT create_ts, hash FROM works
pacetry WHERE name=? ORDER BY create_ts DESC ", name) |
| 409 if err != nil { |
| 410 reportError(w, r, err, "Failed to select.") |
| 411 return |
| 412 } |
| 413 for rows.Next() { |
| 414 var hash string |
| 415 var create_ts time.Time |
| 416 if err := rows.Scan(&create_ts, &hash); err != n
il { |
| 417 log.Printf("Error: failed to fetch from
database: %q", err) |
| 418 continue |
| 419 } |
| 420 tries = append(tries, Try{Hash: hash, CreateTS:
create_ts.Format("2006-02-01")}) |
| 421 } |
| 422 } |
| 423 var code string |
| 424 if len(tries) == 0 { |
| 425 code = DEFAULT_SAMPLE |
| 426 } else { |
| 427 code = getCode(tries[len(tries)-1].Hash) |
| 428 } |
| 429 if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, C
ode: code, Name: name}); err != nil { |
| 430 log.Printf("ERROR: Failed to expand template: %q\n", err
) |
| 431 } |
| 432 } else if r.Method == "POST" { |
| 433 name, err := newWorkspace() |
| 434 if err != nil { |
| 435 http.Error(w, "Failed to create a new workspace.", 500) |
| 436 return |
| 437 } |
| 438 http.Redirect(w, r, "/w/"+name, 302) |
| 439 } |
| 440 } |
| 441 |
297 // hasPreProcessor returns true if any line in the code begins with a # char. | 442 // hasPreProcessor returns true if any line in the code begins with a # char. |
298 func hasPreProcessor(code string) bool { | 443 func hasPreProcessor(code string) bool { |
299 lines := strings.Split(code, "\n") | 444 lines := strings.Split(code, "\n") |
300 for _, s := range lines { | 445 for _, s := range lines { |
301 if strings.HasPrefix(strings.TrimSpace(s), "#") { | 446 if strings.HasPrefix(strings.TrimSpace(s), "#") { |
302 return true | 447 return true |
303 } | 448 } |
304 } | 449 } |
305 return false | 450 return false |
306 } | 451 } |
307 | 452 |
| 453 type TryRequest struct { |
| 454 Code string `json:"code"` |
| 455 Name string `json:"name"` |
| 456 } |
| 457 |
308 // mainHandler handles the GET and POST of the main page. | 458 // mainHandler handles the GET and POST of the main page. |
309 func mainHandler(w http.ResponseWriter, r *http.Request) { | 459 func mainHandler(w http.ResponseWriter, r *http.Request) { |
310 log.Printf("Main Handler: %q\n", r.URL.Path) | 460 log.Printf("Main Handler: %q\n", r.URL.Path) |
311 if r.Method == "GET" { | 461 if r.Method == "GET" { |
312 code := DEFAULT_SAMPLE | 462 code := DEFAULT_SAMPLE |
313 match := directLink.FindStringSubmatch(r.URL.Path) | 463 match := directLink.FindStringSubmatch(r.URL.Path) |
314 if len(match) == 2 && r.URL.Path != "/" { | 464 if len(match) == 2 && r.URL.Path != "/" { |
315 hash := match[1] | 465 hash := match[1] |
316 if db == nil { | 466 if db == nil { |
317 http.NotFound(w, r) | 467 http.NotFound(w, r) |
(...skipping 15 matching lines...) Expand all Loading... |
333 n, err := buf.ReadFrom(r.Body) | 483 n, err := buf.ReadFrom(r.Body) |
334 if err != nil { | 484 if err != nil { |
335 reportError(w, r, err, "Failed to read a request body.") | 485 reportError(w, r, err, "Failed to read a request body.") |
336 return | 486 return |
337 } | 487 } |
338 if n == MAX_TRY_SIZE { | 488 if n == MAX_TRY_SIZE { |
339 err := fmt.Errorf("Code length equal to, or exceeded, %d
", MAX_TRY_SIZE) | 489 err := fmt.Errorf("Code length equal to, or exceeded, %d
", MAX_TRY_SIZE) |
340 reportError(w, r, err, "Code too large.") | 490 reportError(w, r, err, "Code too large.") |
341 return | 491 return |
342 } | 492 } |
343 » » code := string(buf.Bytes()) | 493 » » request := TryRequest{} |
344 » » if hasPreProcessor(code) { | 494 » » if err := json.Unmarshal(buf.Bytes(), &request); err != nil { |
| 495 » » » reportError(w, r, err, "Coulnd't decode JSON.") |
| 496 » » » return |
| 497 » » } |
| 498 » » if hasPreProcessor(request.Code) { |
345 err := fmt.Errorf("Found preprocessor macro in code.") | 499 err := fmt.Errorf("Found preprocessor macro in code.") |
346 reportError(w, r, err, "Preprocessor macros aren't allow
ed.") | 500 reportError(w, r, err, "Preprocessor macros aren't allow
ed.") |
347 return | 501 return |
348 } | 502 } |
349 » » hash, err := expandCode(LineNumbers(code)) | 503 » » hash, err := expandCode(LineNumbers(request.Code)) |
350 if err != nil { | 504 if err != nil { |
351 reportError(w, r, err, "Failed to write the code to comp
ile.") | 505 reportError(w, r, err, "Failed to write the code to comp
ile.") |
352 return | 506 return |
353 } | 507 } |
354 » » writeToDatabase(hash, code) | 508 » » writeToDatabase(hash, request.Code, request.Name) |
355 message, err := doCmd(fmt.Sprintf(RESULT_COMPILE, hash, hash), t
rue) | 509 message, err := doCmd(fmt.Sprintf(RESULT_COMPILE, hash, hash), t
rue) |
356 if err != nil { | 510 if err != nil { |
357 reportError(w, r, err, "Failed to compile the code:\n"+m
essage) | 511 reportError(w, r, err, "Failed to compile the code:\n"+m
essage) |
358 return | 512 return |
359 } | 513 } |
360 linkMessage, err := doCmd(fmt.Sprintf(LINK, hash, hash), true) | 514 linkMessage, err := doCmd(fmt.Sprintf(LINK, hash, hash), true) |
361 if err != nil { | 515 if err != nil { |
362 reportError(w, r, err, "Failed to link the code:\n"+link
Message) | 516 reportError(w, r, err, "Failed to link the code:\n"+link
Message) |
363 return | 517 return |
364 } | 518 } |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
396 reportError(w, r, err, "Failed to serialize a response."
) | 550 reportError(w, r, err, "Failed to serialize a response."
) |
397 return | 551 return |
398 } | 552 } |
399 w.Write(resp) | 553 w.Write(resp) |
400 } | 554 } |
401 } | 555 } |
402 | 556 |
403 func main() { | 557 func main() { |
404 flag.Parse() | 558 flag.Parse() |
405 http.HandleFunc("/i/", imageHandler) | 559 http.HandleFunc("/i/", imageHandler) |
| 560 http.HandleFunc("/w/", workspaceHandler) |
406 http.HandleFunc("/recent/", recentHandler) | 561 http.HandleFunc("/recent/", recentHandler) |
407 http.HandleFunc("/css/", cssHandler) | 562 http.HandleFunc("/css/", cssHandler) |
| 563 http.HandleFunc("/js/", jsHandler) |
408 http.HandleFunc("/", mainHandler) | 564 http.HandleFunc("/", mainHandler) |
409 log.Fatal(http.ListenAndServe(*port, nil)) | 565 log.Fatal(http.ListenAndServe(*port, nil)) |
410 } | 566 } |
OLD | NEW |