From ab112c1c5b372df9876cbe89e8a245986b082f64 Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Mon, 8 Jun 2026 17:11:32 +0700 Subject: [PATCH] First commit --- API_DOCS.md | 357 ++++++++++++++++++++++++++++++++ __pycache__/app.cpython-313.pyc | Bin 0 -> 5260 bytes __pycache__/db.cpython-313.pyc | Bin 0 -> 3178 bytes app.py | 117 +++++++++++ db.py | 70 +++++++ enroll.db | Bin 0 -> 12288 bytes requirements.txt | 1 + templates/playground.html | 297 ++++++++++++++++++++++++++ 8 files changed, 842 insertions(+) create mode 100644 API_DOCS.md create mode 100644 __pycache__/app.cpython-313.pyc create mode 100644 __pycache__/db.cpython-313.pyc create mode 100644 app.py create mode 100644 db.py create mode 100644 enroll.db create mode 100644 requirements.txt create mode 100644 templates/playground.html diff --git a/API_DOCS.md b/API_DOCS.md new file mode 100644 index 0000000..7f6d56a --- /dev/null +++ b/API_DOCS.md @@ -0,0 +1,357 @@ +# ๐Ÿ“– API Enroll Documentation + +**Base URL:** `http://localhost:8080` + +**Endpoint:** `/api/enroll` + +Seluruh operasi CRUD dilakukan melalui **satu endpoint** ini, cukup dengan `GET` dan `POST`. + +--- + +## ๐Ÿ” Authentication + +Semua request ke `/api/enroll` **wajib** menyertakan header: + +``` +Authorization: Bearer +``` + +| Info | Nilai | +|-------------|--------------| +| Token default | `secret123` | +| Letak config | `app.py` โ†’ variabel `API_TOKEN` | + +**Response `401 Unauthorized`** (tanpa header) + +```json +{ + "error": "missing or invalid Authorization header" +} +``` + +**Response `401 Unauthorized`** (token salah) + +```json +{ + "error": "invalid token" +} +``` + +--- + +## ๐Ÿ“Š Database Schema + +| Kolom | Tipe | Keterangan | +|-----------|---------|---------------------------------------------------------| +| `id` | INTEGER | Primary key, auto-increment | +| `type` | TEXT | Tipe enrollment โ€” **hanya boleh `Annually` atau `Monthly`** | +| `service` | TEXT | Paket layanan โ€” **hanya boleh `Lite`, `Value`, atau `Pro`** | +| `user` | TEXT | Nama pengguna (contoh: `alice`) | + +--- + +## ๐Ÿ  Routes + +| Route | Method | Auth | Fungsi | +|----------------|--------|------|--------------------------------| +| `/` | GET | โŒ | Halaman playground (UI web) | +| `/api/enroll` | GET | โœ… | List / Detail enrollment | +| `/api/enroll` | POST | โœ… | Add / Edit / Remove enrollment | + +--- + +## 1๏ธโƒฃ List โ€” Mendapatkan semua data + +**Request** + +``` +GET /api/enroll +Authorization: Bearer secret123 +``` + +**Contoh curl** + +```bash +curl -H "Authorization: Bearer secret123" \ + http://localhost:8080/api/enroll +``` + +**Response `200 OK`** + +```json +{ + "data": [ + { + "id": 2, + "type": "Monthly", + "service": "Lite", + "user": "bob" + }, + { + "id": 1, + "type": "Annually", + "service": "Pro", + "user": "alice" + } + ] +} +``` + +--- + +## 2๏ธโƒฃ Detail โ€” Mendapatkan satu data + +**Request** + +``` +GET /api/enroll?id= +Authorization: Bearer secret123 +``` + +**Parameter Query** + +| Parameter | Tipe | Wajib | Keterangan | +|-----------|---------|-------|---------------| +| `id` | integer | โœ… | ID enrollment | + +**Contoh curl** + +```bash +curl -H "Authorization: Bearer secret123" \ + http://localhost:8080/api/enroll?id=1 +``` + +**Response `200 OK`** + +```json +{ + "data": { + "id": 1, + "type": "Annually", + "service": "Pro", + "user": "alice" + } +} +``` + +**Response `404 Not Found`** + +```json +{ + "error": "not found" +} +``` + +--- + +## 3๏ธโƒฃ Add โ€” Menambah data baru + +**Request** + +``` +POST /api/enroll +Authorization: Bearer secret123 +Content-Type: application/x-www-form-urlencoded +``` + +**Form Data** + +| Field | Tipe | Wajib | Keterangan | +|-----------|--------|-------|---------------------------------------------------------| +| `type` | string | โœ… | **Hanya boleh `Annually` atau `Monthly`** | +| `service` | string | โœ… | **Hanya boleh `Lite`, `Value`, atau `Pro`** | +| `user` | string | โœ… | Nama pengguna | + +> โš ๏ธ Jangan kirim field `id` โ€” kalau `id` terkirim, ini akan dianggap **Edit**. + +**Contoh curl** + +```bash +curl -X POST http://localhost:8080/api/enroll \ + -H "Authorization: Bearer secret123" \ + -d "type=Annually" \ + -d "service=Pro" \ + -d "user=alice" +``` + +**Response `201 Created`** + +```json +{ + "message": "created", + "id": 1 +} +``` + +**Response `400 Bad Request`** (type/service tidak valid) + +```json +{ + "error": "type must be one of: Annually, Monthly" +} +``` + +```json +{ + "error": "service must be one of: Lite, Value, Pro" +} +``` + +--- + +## 4๏ธโƒฃ Edit โ€” Mengubah data yang sudah ada + +**Request** + +``` +POST /api/enroll +Authorization: Bearer secret123 +Content-Type: application/x-www-form-urlencoded +``` + +**Form Data** + +| Field | Tipe | Wajib | Keterangan | +|-----------|---------|-------|---------------------------------------------------------| +| `id` | integer | โœ… | ID yang ingin diubah | +| `type` | string | โœ… | **Hanya boleh `Annually` atau `Monthly`** (baru) | +| `service` | string | โœ… | **Hanya boleh `Lite`, `Value`, atau `Pro`** (baru) | +| `user` | string | โœ… | Nama pengguna (baru) | + +> ๐Ÿ’ก Cukup sertakan `id` pada form POST, sistem otomatis tahu ini adalah operasi **Edit**. + +**Contoh curl** + +```bash +curl -X POST http://localhost:8080/api/enroll \ + -H "Authorization: Bearer secret123" \ + -d "id=1" \ + -d "type=Monthly" \ + -d "service=Value" \ + -d "user=bob" +``` + +**Response `200 OK`** + +```json +{ + "message": "updated", + "id": 1 +} +``` + +**Response `400 Bad Request`** (type/service tidak valid) + +```json +{ + "error": "type must be one of: Annually, Monthly" +} +``` + +```json +{ + "error": "service must be one of: Lite, Value, Pro" +} +``` + +--- + +## 5๏ธโƒฃ Remove โ€” Menghapus data + +**Request** + +``` +POST /api/enroll +Authorization: Bearer secret123 +Content-Type: application/x-www-form-urlencoded +``` + +**Form Data** + +| Field | Tipe | Wajib | Keterangan | +|-----------|---------|-------|---------------------------------------------| +| `id` | integer | โœ… | ID yang ingin dihapus | +| `action` | string | โœ… | Harus bernilai `"remove"` (sebagai penanda) | + +**Contoh curl** + +```bash +curl -X POST http://localhost:8080/api/enroll \ + -H "Authorization: Bearer secret123" \ + -d "id=1" \ + -d "action=remove" +``` + +**Response `200 OK`** + +```json +{ + "message": "removed", + "id": 1 +} +``` + +**Response `400 Bad Request`** (tanpa id) + +```json +{ + "error": "id is required for remove" +} +``` + +--- + +## ๐Ÿ“‹ Ringkasan Logika POST + +``` +POST /api/enroll +โ”‚ +โ”œโ”€โ”€ action = "remove" โ†’ hapus data (butuh id) +โ”œโ”€โ”€ ada id โ†’ edit data (butuh type, service, user) +โ””โ”€โ”€ tidak ada id โ†’ tambah data baru (butuh type, service, user) +``` + +## ๐Ÿ”‘ Ringkasan Response Error + +| Status | Kondisi | Body | +|--------|--------------------------------|---------------------------------------------| +| `401` | Tanpa header Authorization | `{"error": "missing or invalid โ€ฆ"}` | +| `401` | Token salah | `{"error": "invalid token"}` | +| `400` | Field wajib kosong | `{"error": "โ€ฆ are required"}` | +| `400` | Type bukan Annually/Monthly | `{"error": "type must be one of: Annually, Monthly"}` | +| `400` | Service bukan Lite/Value/Pro | `{"error": "service must be one of: Lite, Value, Pro"}` | +| `404` | ID tidak ditemukan | `{"error": "not found"}` | + +## ๐ŸŽฎ Playground UI + +Setelah server jalan, buka browser ke: + +``` +http://localhost:8080 +``` + +Anda akan diarahkan ke halaman **Playground** yang memudahkan pengujian semua endpoint langsung dari browser: + +1. **Isi token** di bar atas (default: `secret123`) +2. Status otomatis berubah: โœ… authenticated / โŒ wrong token +3. Isi form, klik tombol, lihat response JSON secara realtime + +--- + +## ๐Ÿš€ Quick Start + +```bash +# masuk ke folder project +cd api-playground + +# install dependency +pip3 install -r requirements.txt + +# jalankan server +python3 app.py + +# buka di browser +# http://localhost:8080 +``` + +Server akan berjalan di `http://0.0.0.0:8080` dengan `reloader=True` (auto-restart saat kode diubah). diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfb043d09f1b25edafd35767e71ed6a7ee45071e GIT binary patch literal 5260 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 literal 0 HcmV?d00001 diff --git a/__pycache__/db.cpython-313.pyc b/__pycache__/db.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc1f3064bd09fabe7b50bbfb5ab1501faf75cfcb GIT binary patch literal 3178 zcmbVO-*3}a6u$nE#EIMV2ZeTH15=o=Cqt zUpZg(oTQv9nLbH79`?!%_Q@>fWY32P`(-cKfb0Xy%N*Dm*$=i>4uB2HJlMJkDpd0~ zSsuUXS9NS;v&S=IA==`?x;~xCW@jzmfT8Cnv$G+_Vq;ofwLHVA?6hhzL)b_=4|XgA zCvvQ3z&u1(oV#G9()*QffYXYulreF?MBlguFy1;6C|h2WK!;F>iibRw&-^M2ul2d* zOB=ebrt_9B(vuttD}5Ft88a4-jhW{H5ONt8*j6Rb}=CykuinaYi&GAVp4 zbxrG3Z%nCJ%c*+4Gc~0ho64qUC$KTCXF4-u$ERj3ZbHo`$riW)w&39~4}dA4H+$! zlnawv z@@cx3PiDq&3tSO$&EsGQvAgb_DMVIS?r!W(Y!&$$xwpvY@vqh+kN+#B-PkmKF(trj<~ z=F^iPA-I`bvKcL%4>7pTzJQuEEee|i8b~0Aftsvl=95mInRp*e?T5p>2&O^w*K=Q-)fRt z`E_@A51TGm8{8qHVsrEaoT?mA395P}CQujU%I~&3(XhkzJkjm&bTU$)D%BV|1Puj5 z59;+mRW`ot6D5&QaZc#2!i1r#HWPR+JOjkMB=<=z<8ZHaN^~ zFcmfe!G(_bj^*Yv&qklcih=0Mfb={d6$474w^S5!u|Hb&-tSsu?wwv{4?k%F<5UTG zlsn2*@&nwd$^esKsxvS`CFm~3z$?6b@FolHSa4aFpLVI_IKz^Z(eBeE>uTvEMY|6|7?pg*KnTPC@gns6zpT&}` zO5CKUv7Nj`ZA;Y>NM$lEJ4a#1V{n)kz-(n_h3j4M2Ol>rbNkjk4DT)Uu5w$YYc96J z2N%xGpIc^I9C}DmxPv6FT_6Q;s?x(I*!R%WIC7$kb?JeI#O^>3yFEQl!#bZBijY24 z>PfPw2zFC}%r8?x%D|z6c*4f&z)kQSKndyGm}E6RLJTQD8}F@9^J)fKL#YxvAnFS^ z%q}n$tey6-!u6G~4!!j-9`CzIyG^310DTQxR>k)}2@s%5?SSqMBIKaEKLTP8=x&ty zcj2QQ_=ujHP2;J1%9O=R0ytl8LED*hNl|VK4PhZ ZYeAN3TMKg32WxwLRKt1`OEuUJ{};TwNZkMc literal 0 HcmV?d00001 diff --git a/app.py b/app.py new file mode 100644 index 0000000..0b55dc2 --- /dev/null +++ b/app.py @@ -0,0 +1,117 @@ +from bottle import Bottle, request, response, static_file +import json +import os +import db + +app = Bottle() +db.init_db() + +# โ”€โ”€ auth config โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# Token simpel, mudah diingat. Ganti sesuai kebutuhan. +API_TOKEN = "secret123" + + +# โ”€โ”€ helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +def json_response(data, status=200): + response.content_type = "application/json" + response.status = status + return json.dumps(data) + + +def check_auth(): + """ + Cek Bearer token. + Return None jika OK, atau string JSON error jika gagal. + """ + auth = request.get_header("Authorization", "") + if not auth.startswith("Bearer "): + return json.dumps({"error": "missing or invalid Authorization header"}) + token = auth.split(" ", 1)[1].strip() + if token != API_TOKEN: + return json.dumps({"error": "invalid token"}) + return None # โœ… token valid + + +# โ”€โ”€ static playground page (tanpa auth) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +@app.get("/") +def index(): + return static_file("playground.html", root=os.path.join(os.path.dirname(__file__), "templates")) + + +# โ”€โ”€ API: /api/enroll (butuh auth) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +@app.get("/api/enroll") +def api_enroll(): + """ + GET /api/enroll โ†’ list all + GET /api/enroll?id=3 โ†’ detail id=3 + """ + err = check_auth() + if err: + return json_response(json.loads(err), 401) + + enroll_id = request.query.id + if enroll_id: + row = db.get_enroll(int(enroll_id)) + if not row: + return json_response({"error": "not found"}, 404) + return json_response({"data": row}) + rows = db.list_enrolls() + return json_response({"data": rows}) + + +@app.post("/api/enroll") +def api_enroll_post(): + """ + POST /api/enroll (form fields + Bearer token) + id โ†’ if present โ†’ edit, otherwise โ†’ add + type + service + user + action โ†’ "remove" to delete (needs id) + """ + err = check_auth() + if err: + return json_response(json.loads(err), 401) + + action = request.forms.get("action", "").strip() + enroll_id = request.forms.get("id", "").strip() + type_ = request.forms.get("type", "").strip() + service = request.forms.get("service", "").strip() + user = request.forms.get("user", "").strip() + + # โ”€โ”€ remove โ”€โ”€ + if action == "remove": + if not enroll_id: + return json_response({"error": "id is required for remove"}, 400) + db.remove_enroll(int(enroll_id)) + return json_response({"message": "removed", "id": int(enroll_id)}) + + # โ”€โ”€ validate type โ”€โ”€ + if type_ not in db.VALID_TYPES: + return json_response( + {"error": f"type must be one of: {', '.join(db.VALID_TYPES)}"}, 400 + ) + + # โ”€โ”€ validate service โ”€โ”€ + if service not in db.VALID_SERVICES: + return json_response( + {"error": f"service must be one of: {', '.join(db.VALID_SERVICES)}"}, 400 + ) + + # โ”€โ”€ edit โ”€โ”€ + if enroll_id: + if not type_ or not service or not user: + return json_response({"error": "type, service, user are required"}, 400) + db.edit_enroll(int(enroll_id), type_, service, user) + return json_response({"message": "updated", "id": int(enroll_id)}) + + # โ”€โ”€ add โ”€โ”€ + if not type_ or not service or not user: + return json_response({"error": "type, service, user are required"}, 400) + new_id = db.add_enroll(type_, service, user) + return json_response({"message": "created", "id": new_id}, 201) + + +# โ”€โ”€ run โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8080, debug=True, reloader=True) diff --git a/db.py b/db.py new file mode 100644 index 0000000..53ba725 --- /dev/null +++ b/db.py @@ -0,0 +1,70 @@ +import sqlite3 +import os + +DB_PATH = os.path.join(os.path.dirname(__file__), "enroll.db") + +# โ”€โ”€ allowed values โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +VALID_TYPES = ("Annually", "Monthly") +VALID_SERVICES = ("Lite", "Value", "Pro") + + +def get_conn(): + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + return conn + + +def init_db(): + conn = get_conn() + conn.execute(""" + CREATE TABLE IF NOT EXISTS enroll ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + type TEXT NOT NULL, + service TEXT NOT NULL, + user TEXT NOT NULL + ) + """) + conn.commit() + conn.close() + + +def list_enrolls(): + conn = get_conn() + rows = conn.execute("SELECT * FROM enroll ORDER BY id DESC").fetchall() + conn.close() + return [dict(r) for r in rows] + + +def get_enroll(enroll_id): + conn = get_conn() + row = conn.execute("SELECT * FROM enroll WHERE id = ?", (enroll_id,)).fetchone() + conn.close() + return dict(row) if row else None + + +def add_enroll(type_, service, user): + conn = get_conn() + cur = conn.execute( + "INSERT INTO enroll (type, service, user) VALUES (?, ?, ?)", + (type_, service, user), + ) + conn.commit() + conn.close() + return cur.lastrowid + + +def edit_enroll(enroll_id, type_, service, user): + conn = get_conn() + conn.execute( + "UPDATE enroll SET type = ?, service = ?, user = ? WHERE id = ?", + (type_, service, user, enroll_id), + ) + conn.commit() + conn.close() + + +def remove_enroll(enroll_id): + conn = get_conn() + conn.execute("DELETE FROM enroll WHERE id = ?", (enroll_id,)) + conn.commit() + conn.close() diff --git a/enroll.db b/enroll.db new file mode 100644 index 0000000000000000000000000000000000000000..dcf49b75e65794a67d4239402b19c9b6d3da83e1 GIT binary patch literal 12288 zcmeI&%}T>S5C`zxL=+09-b!zCY-#Y~3m9dQV4AjZ3+5!Tu24u*^HD*;Lw!qq4j;gc zp<;sQr69;!%}4di0t%?m}h%WM%9bi@uA=j@n>F;)&!k4j~fMbi4fd>7%AfvSl1svr(&1J)>TWzQp{d zT)4gzXCk0+;0~Q2q;nCHGnFIP*A<4smv(Ja@o=Yzq_~vikMx*&UOgdK+5LQ`eo9&C zoJNh$cl68NTsQs9dinm?>L)=l2tWV=5P$##AOHafKmY;|fB*#kM4;^ddHap&69EAT fKmY;|fB*y_009U<00Izzz&{dbakI;-e-x~D*R^Tx literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fc1fc4f --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +bottle==0.13.4 diff --git a/templates/playground.html b/templates/playground.html new file mode 100644 index 0000000..8beb4da --- /dev/null +++ b/templates/playground.html @@ -0,0 +1,297 @@ + + + + + +API Enroll Playground + + + + +

๐Ÿš€ API Enroll Playground

+ + +
+ + + no token + default: secret123 +
+ +
+ + +
+

โž• Add (POST)

+ + + + + +
+ +
+
โ€”
+
+ + +
+

๐Ÿ“‹ List (GET)

+
+ +
+
โ€”
+
+ + +
+

๐Ÿ” Detail (GET)

+ + +
+ +
+
โ€”
+
+ + +
+

โœ๏ธ Edit (POST)

+ + + + + + +
+ +
+
โ€”
+
+ + +
+

๐Ÿ—‘๏ธ Remove (POST)

+ + +
+ +
+
โ€”
+
+ + +
+

๐Ÿ“Š Table Preview

+
+ +
+ + + +
IDTypeServiceUser
Click Refresh
+
+ +
+ + + +