diff options
author | Blaster4385 <blaster4385@tablaster.dev> | 2024-02-19 20:42:57 +0530 |
---|---|---|
committer | Blaster4385 <blaster4385@tablaster.dev> | 2024-02-21 23:52:45 +0530 |
commit | 86ed77b9cc63d9fd8d61a0d3b56ef1307be7e007 (patch) | |
tree | 4c6dfcbb5737db8b0042f470ba06d3761f0d0d58 /server | |
parent | 23de613393d2399babe3a7b4d2fb77d772fcfa87 (diff) |
refactor: Rewrite server in go
Diffstat (limited to 'server')
-rw-r--r-- | server/.gitignore | 200 | ||||
-rwxr-xr-x | server/bun.lockb | bin | 79826 -> 0 bytes | |||
-rw-r--r-- | server/controllers/bin.js | 100 | ||||
-rw-r--r-- | server/controllers/health.js | 7 | ||||
-rw-r--r-- | server/go.mod | 10 | ||||
-rw-r--r-- | server/go.sum | 8 | ||||
-rw-r--r-- | server/index.js | 18 | ||||
-rw-r--r-- | server/main.go | 142 | ||||
-rw-r--r-- | server/package.json | 23 | ||||
-rw-r--r-- | server/routes/bin.js | 11 | ||||
-rw-r--r-- | server/routes/health.js | 8 | ||||
-rw-r--r-- | server/tsconfig.json | 22 |
12 files changed, 185 insertions, 364 deletions
diff --git a/server/.gitignore b/server/.gitignore index ce007ac..c2cb318 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -1,175 +1,25 @@ -# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore - -# Logs - -logs -_.log -npm-debug.log_ -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Caches - -.cache - -# Diagnostic reports (https://nodejs.org/api/report.html) - -report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json - -# Runtime data - -pids -_.pid -_.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover - -lib-cov - -# Coverage directory used by tools like istanbul - -coverage -*.lcov - -# nyc test coverage - -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) - -.grunt - -# Bower dependency directory (https://bower.io/) - -bower_components - -# node-waf configuration - -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) - -build/Release - -# Dependency directories - -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) - -web_modules/ - -# TypeScript cache - -*.tsbuildinfo - -# Optional npm cache directory - -.npm - -# Optional eslint cache - -.eslintcache - -# Optional stylelint cache - -.stylelintcache - -# Microbundle cache - -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history - -.node_repl_history - -# Output of 'npm pack' - -*.tgz - -# Yarn Integrity file - -.yarn-integrity - -# dotenv environment variable files - -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) - -.parcel-cache - -# Next.js build output - -.next -out - -# Nuxt.js build / generate output - -.nuxt -dist - -# Gatsby files - -# Comment in the public line in if your project uses Gatsby and not Next.js - -# https://nextjs.org/blog/next-9-1#public-directory-support - -# public - -# vuepress build output - -.vuepress/dist - -# vuepress v2.x temp and cache directory - -.temp - -# Docusaurus cache and generated files - -.docusaurus - -# Serverless directories - -.serverless/ - -# FuseBox cache - -.fusebox/ - -# DynamoDB Local files - -.dynamodb/ - -# TernJS port file - -.tern-port - -# Stores VSCode versions used for testing VSCode extensions - -.vscode-test - -# yarn v2 - -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -# IntelliJ based IDEs -.idea - -# Finder (MacOS) folder config -.DS_Store
\ No newline at end of file +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +minibin + +# Database +*.db + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work diff --git a/server/bun.lockb b/server/bun.lockb Binary files differdeleted file mode 100755 index 0ba4f0e..0000000 --- a/server/bun.lockb +++ /dev/null diff --git a/server/controllers/bin.js b/server/controllers/bin.js deleted file mode 100644 index 882ecef..0000000 --- a/server/controllers/bin.js +++ /dev/null @@ -1,100 +0,0 @@ -import sqlite3 from "sqlite3"; - -const dbPath = "./database/bins.db"; - -function createBinTable() { - const db = new sqlite3.Database(dbPath, (err) => { - if (err) { - console.error("Error connecting to database:", err.message); - return; - } - - const sql = `CREATE TABLE IF NOT EXISTS bin ( - id TEXT PRIMARY KEY, - html_content TEXT - )`; - - db.run(sql, (err) => { - if (err) { - console.error("Error creating table:", err.message); - } else { - console.log("`bin` table created successfully"); - } - - db.close((err) => { - if (err) { - console.error("Error closing database:", err.message); - } - }); - }); - }); -} - -const createBin = (req, res) => { - console.log(`createBin: ${req.body}`); - const { html_content } = req.body; - const id = Math.random().toString(36).substring(7); - - const db = new sqlite3.Database(dbPath, (err) => { - if (err) { - console.error("Error connecting to database:", err.message); - throw err; // Re-throw for async handling - } - - const sql = `INSERT INTO bin (id, html_content) VALUES (?, ?)`; - - db.run(sql, [id, html_content], (err) => { - if (err) { - console.error("Error creating entry:", err.message); - throw err; // Re-throw for async handling - } else { - res.status(201).json({ id }); - console.log("Entry created successfully:", id); - } - - db.close((err) => { - if (err) { - console.error("Error closing database:", err.message); - } - }); - }); - }); -}; - -const getBin = async (req, res) => { - const { id } = req.params; - if (!id) { - throw new Error("Missing required parameter: id"); // Throw error for missing ID - } - - const db = new sqlite3.Database(dbPath, (err) => { - if (err) { - console.error("Error connecting to database:", err.message); - throw err; // Re-throw for async handling - } - - const sql = `SELECT * FROM bin WHERE id = ?`; - - db.get(sql, [id], (err, row) => { - if (err) { - console.error("Error retrieving entry:", err.message); - throw err; // Re-throw for async handling - } else if (!row) { - console.log("Entry not found:", id); - return null; // Handle case where no entry exists - } - - res.status(200).json(row); - console.log("Entry retrieved:", id); - db.close((err) => { - if (err) { - console.error("Error closing database:", err.message); - } - }); - - return row; - }); - }); -}; - -export default { createBinTable, createBin, getBin }; diff --git a/server/controllers/health.js b/server/controllers/health.js deleted file mode 100644 index d31c489..0000000 --- a/server/controllers/health.js +++ /dev/null @@ -1,7 +0,0 @@ -const healthCheck = async (req, res) => { - return res.json({ - uptime: process.uptime(), - }); -}; - -export default { healthCheck }; diff --git a/server/go.mod b/server/go.mod new file mode 100644 index 0000000..c15dae4 --- /dev/null +++ b/server/go.mod @@ -0,0 +1,10 @@ +module minibin + +go 1.22.0 + +require ( + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/rs/cors v1.10.1 // indirect +) diff --git a/server/go.sum b/server/go.sum new file mode 100644 index 0000000..a0572d7 --- /dev/null +++ b/server/go.sum @@ -0,0 +1,8 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= diff --git a/server/index.js b/server/index.js deleted file mode 100644 index 57546fd..0000000 --- a/server/index.js +++ /dev/null @@ -1,18 +0,0 @@ -import Express from "express"; -import cors from "cors"; -import dotenv from "dotenv"; - -import binRoutes from "./routes/bin.js"; -import healthRoutes from "./routes/health.js"; - -const app = Express(); -dotenv.config(); - -app.use(cors()); -app.use(Express.json()); -app.use("/", healthRoutes); -app.use("/bin", binRoutes); - -const PORT = process.env.PORT; - -app.listen(PORT, () => console.log(`Server running on PORT: ${PORT}`)); diff --git a/server/main.go b/server/main.go new file mode 100644 index 0000000..4618e5b --- /dev/null +++ b/server/main.go @@ -0,0 +1,142 @@ +package main + +import ( + "database/sql" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "math/rand" + "net/http" + "strings" + "time" + + _ "github.com/mattn/go-sqlite3" + "github.com/rs/cors" +) + +var db *sql.DB +var port int +var dbFilePath string + +type Bin struct { + Content string `json:"content"` + Language string `json:"language"` +} + +const ( + shortIDCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + shortIDLength = 8 +) + +func main() { + flag.IntVar(&port, "port", 8080, "Port number for the server (default is 8080)") + flag.StringVar(&dbFilePath, "db", "./minibin.db", "Database file path") + flag.Parse() + + setupServer() +} + +func setupServer() { + mux := http.NewServeMux() + mux.HandleFunc("/bin", postBin) + mux.HandleFunc("/bin/", getBin) + handler := cors.Default().Handler(mux) + serverAddr := fmt.Sprintf(":%d", port) + log.Printf("Server listening on port %d...\n", port) + initDatabase() + log.Fatal(http.ListenAndServe(serverAddr, handler)) +} + +func initDatabase() { + var err error + db, err = sql.Open("sqlite3", dbFilePath) + if err != nil { + log.Fatal(err) + } + + err = createTable() + if err != nil { + log.Fatal(err) + } +} + +func postBin(w http.ResponseWriter, r *http.Request) { + handleRequestMethod(w, r, "POST", func() { + body, err := ioutil.ReadAll(r.Body) + if handleError(w, err, http.StatusInternalServerError) { + return + } + var bin Bin + err = json.Unmarshal(body, &bin) + if handleError(w, err, http.StatusBadRequest) { + return + } + id := generateShortID() + handleError(w, saveBin(id, bin), http.StatusInternalServerError) + respondWithJSON(w, http.StatusOK, map[string]string{"id": id, "content": bin.Content, "language": bin.Language}) + }) +} + +func getBin(w http.ResponseWriter, r *http.Request) { + handleRequestMethod(w, r, "GET", func() { + id := strings.TrimPrefix(r.URL.Path, "/bin/") + bin, err := getBinById(id) + handleError(w, err, http.StatusInternalServerError) + respondWithJSON(w, http.StatusOK, bin) + }) +} + +func handleRequestMethod(w http.ResponseWriter, r *http.Request, expectedMethod string, handler func()) { + if r.Method != expectedMethod { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + handler() +} + +func handleError(w http.ResponseWriter, err error, statusCode int) bool { + if err != nil { + http.Error(w, err.Error(), statusCode) + return true + } + return false +} + +func respondWithJSON(w http.ResponseWriter, statusCode int, data interface{}) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(statusCode) + json.NewEncoder(w).Encode(data) +} + +func createTable() error { + _, err := db.Exec(` + CREATE TABLE IF NOT EXISTS bins ( + id TEXT PRIMARY KEY, + content TEXT, + language TEXT + ) +`) + return err +} + +func getBinById(id string) (Bin, error) { + var bin Bin + err := db.QueryRow("SELECT content, language FROM bins WHERE id = ?", id).Scan(&bin.Content, &bin.Language) + return bin, err +} + +func saveBin(id string, bin Bin) error { + _, err := db.Exec("INSERT INTO bins (id, content, language) VALUES (?, ?, ?)", id, bin.Content, bin.Language) + return err +} + +func generateShortID() string { + rand.Seed(time.Now().UnixNano()) + id := make([]byte, shortIDLength) + for i := range id { + id[i] = shortIDCharset[rand.Intn(len(shortIDCharset))] + } + return string(id) +} diff --git a/server/package.json b/server/package.json deleted file mode 100644 index a74b362..0000000 --- a/server/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "minibun-server", - "version": "1.0.0", - "description": "", - "main": "index.js", - "type": "module", - "scripts": { - "start": "nodemon index.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC", - "devDependencies": { - "cors": "^2.8.5", - "dotenv": "^16.4.4", - "nodemon": "^3.0.3" - }, - "dependencies": { - "express": "^4.18.2", - "sqlite3": "^5.1.7" - } -}
\ No newline at end of file diff --git a/server/routes/bin.js b/server/routes/bin.js deleted file mode 100644 index 38b2ddc..0000000 --- a/server/routes/bin.js +++ /dev/null @@ -1,11 +0,0 @@ -import Express from "express"; -import bin from "../controllers/bin.js"; - -const router = Express.Router(); - -bin.createBinTable(); - -router.get("/:id", bin.getBin); -router.post("/", bin.createBin); - -export default router; diff --git a/server/routes/health.js b/server/routes/health.js deleted file mode 100644 index 04275c9..0000000 --- a/server/routes/health.js +++ /dev/null @@ -1,8 +0,0 @@ -import Express from "express"; -import health from "../controllers/health.js"; - -const router = Express.Router(); - -router.get("/health", health.healthCheck); - -export default router; diff --git a/server/tsconfig.json b/server/tsconfig.json deleted file mode 100644 index 761170e..0000000 --- a/server/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "lib": ["ESNext"], - "target": "ESNext", - "module": "ESNext", - "moduleDetection": "force", - "jsx": "react-jsx", - "allowJs": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "noEmit": true, - - /* Linting */ - "skipLibCheck": true, - "strict": true, - "noFallthroughCasesInSwitch": true, - "forceConsistentCasingInFileNames": true - } - }
\ No newline at end of file |