notepad

Check-in [5cdaff3a2c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Bringup; we have login and initial (empty) note list
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | master | trunk
Files: files | file ages | folders
SHA3-256:5cdaff3a2c7648ce899efccf57ed8011becc6ad510d1226bd4b9eec53fb50191
User & Date: vandys 2018-10-11 23:56:26
Context
2018-10-12
00:47
Bringup; insert first note check-in: f56588fac2 user: vandys tags: master, trunk
2018-10-11
23:56
Bringup; we have login and initial (empty) note list check-in: 5cdaff3a2c user: vandys tags: master, trunk
22:05
Clean "fs extras" check-in: d10f9fd4be user: vandys tags: master, trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added config.py.













>
>
>
>
>
>
1
2
3
4
5
6
#
# config.py
#
# Basic setup for notepad
#
DBNAME= "notes-4323zz.db"

Changes to index.html.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
43
44
45
46
47
48
49
50




51
52
53
54
55
56
57
58
59
<!DOCTYPE html>
<html lang="en" manifest="cache.manifest.php">
<!-- manifest="cache.manifest.php" --> 
<head>
    <title>Cloud Notepad</title>
	
    <meta charset="utf-8" />
    <!-- <meta http-equiv="pragma" content="no-cache" /> -->

    <meta name="viewport" content="user-scalable=no, width=device-width,
     initial-scale=1.0, maximum-scale=1.0" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <link rel="stylesheet" type="text/css" href="css/main.css" />
</head>

<body>


<div id="mainmenu">
    <div id="tools">
	<span onclick="tool_new()">Add New</span>
	<span onclick="tool_sync()">Sync Entries</span>
	<span onclick="tool_logout()">Logout</span>
	<span onclick="tool_clear()">Clear Local Storage</span>
    </div>

    <div id="entries">
    </div>
</div>

<div id="editor">
................................................................................
    <input type="text" id="user" placeholder="User name">
    <br>
    <input type="password" id="password" placeholder="password">
    <br>
    <button onclick="try_login()">Login</button>
</div>

<script src="js/main.js">





</body>

<script>
// Hook for initial JS execution
document.onready = startup;
</script>

</html>

<
<








<
<




>



|
|
|
|







 







|
>
>
>
>



<
<
<
<
<

1


2
3
4
5
6
7
8
9


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54





55
<!DOCTYPE html>


<head>
    <title>Cloud Notepad</title>
	
    <meta charset="utf-8" />
    <!-- <meta http-equiv="pragma" content="no-cache" /> -->

    <meta name="viewport" content="user-scalable=no, width=device-width,
     initial-scale=1.0, maximum-scale=1.0" />


    <link rel="stylesheet" type="text/css" href="css/main.css" />
</head>

<body>


<div id="mainmenu">
    <div id="tools">
	<button onclick="tool_new()">Add New</button>
	<button onclick="sync()">Sync Entries</button>
	<button onclick="tool_logout()">Logout</button>
	<button onclick="tool_clear()">Clear Local Storage</button>
    </div>

    <div id="entries">
    </div>
</div>

<div id="editor">
................................................................................
    <input type="text" id="user" placeholder="User name">
    <br>
    <input type="password" id="password" placeholder="password">
    <br>
    <button onclick="try_login()">Login</button>
</div>

<script src="js/main.js"></script>
<script>
window.onload = startup;
</script>


</body>






</html>

Changes to js/main.js.

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
..
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
...
249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
264
...
287
288
289
290
291
292
293



294
295
296
297
298
299

// When we need to fake a timestamp to offer sequential, new
//  changes to our server when our own clock is borked.
let tm_off = 0.0;

// Try to pull in current store on startup
function startup() {
    if (localStorage.getItem("tm") == null) {
	// Initial state
	localStorage.tm = "0.0";
	server_tm = 0.0;
	// They need to log in
	login.style.display = "block";
    } else {
	// Saved state
	server_tm = parseFloat(localStorage.tm);

	to_menu();
    }
    sync();
}

// Mint a new key for a new note
function new_key() {
    while (true) {
	let key = "note" +
	    Math.floor(Math.random() * 1000000000.0).toString();
................................................................................
function update_entries() {
    // Remove old list
    while (entries.hasChildNodes()) {
	entries.removeChild(entries.lastChild);
    }

    // Traverse localStorage, assemble notes
    ents = [];
    for (let idx = 0; idx < localStorage.length; ++idx) {
	let key = localStorage.key(idx);
	if (!key.startsWith("note")) {
	    // Ignore non-note state
	    continue;
	}

................................................................................
    udpate_entries();
    to_menu();
}

// Create a delta of changes, so we only have to submit back things
//  which have changed WRT our last update from the server
function save_notes() {
    const upd = {}
    for (let idx = 0; idx < localStorage.length; ++idx) {
	let key = localStorage.key(idx);
	if (key.startsWith("note")) {
	    let ent = JSON.parse(localStorage[key]);
	    if (ent.tm > server_tm) {
		upd[key] = ent;
	    }
................................................................................
	const deltas = JSON.parse(req.responseText);
	update_notes(deltas);
	update_entries();
    }

    // Submit our current content, including credentials
    const onotes = save_notes();

    onotes.user = user.value;
    onotes.password = password.value;
    req.send(JSON.stringify(onotes));
}

// Log out (clear localStorage and PHP server side)
function tool_logout() {
    if (!confirm("Clear notes session?")) {
	return;
................................................................................
	if (req.status != 200) {
	    alert("Login failed, try again");
	    return;
	}

	// User/pass is OK, so go to main UI
	login.style.display = "none";



	to_menu();
    }

    // Submit our current content
    req.send(JSON.stringify(creds));
}







|








>


<







 







|







 







|







 







>
|
|







 







>
>
>






23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
..
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
...
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
...
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303

// When we need to fake a timestamp to offer sequential, new
//  changes to our server when our own clock is borked.
let tm_off = 0.0;

// Try to pull in current store on startup
function startup() {
    if (localStorage.getItem("user") == null) {
	// Initial state
	localStorage.tm = "0.0";
	server_tm = 0.0;
	// They need to log in
	login.style.display = "block";
    } else {
	// Saved state
	server_tm = parseFloat(localStorage.tm);
	sync();
	to_menu();
    }

}

// Mint a new key for a new note
function new_key() {
    while (true) {
	let key = "note" +
	    Math.floor(Math.random() * 1000000000.0).toString();
................................................................................
function update_entries() {
    // Remove old list
    while (entries.hasChildNodes()) {
	entries.removeChild(entries.lastChild);
    }

    // Traverse localStorage, assemble notes
    const ents = [];
    for (let idx = 0; idx < localStorage.length; ++idx) {
	let key = localStorage.key(idx);
	if (!key.startsWith("note")) {
	    // Ignore non-note state
	    continue;
	}

................................................................................
    udpate_entries();
    to_menu();
}

// Create a delta of changes, so we only have to submit back things
//  which have changed WRT our last update from the server
function save_notes() {
    const upd = {};
    for (let idx = 0; idx < localStorage.length; ++idx) {
	let key = localStorage.key(idx);
	if (key.startsWith("note")) {
	    let ent = JSON.parse(localStorage[key]);
	    if (ent.tm > server_tm) {
		upd[key] = ent;
	    }
................................................................................
	const deltas = JSON.parse(req.responseText);
	update_notes(deltas);
	update_entries();
    }

    // Submit our current content, including credentials
    const onotes = save_notes();
    onotes.tm = server_tm;
    onotes.user = localStorage.user;
    onotes.password = localStorage.password;
    req.send(JSON.stringify(onotes));
}

// Log out (clear localStorage and PHP server side)
function tool_logout() {
    if (!confirm("Clear notes session?")) {
	return;
................................................................................
	if (req.status != 200) {
	    alert("Login failed, try again");
	    return;
	}

	// User/pass is OK, so go to main UI
	login.style.display = "none";
	localStorage.user = user.value;
	localStorage.password = password.value;
	sync();
	to_menu();
    }

    // Submit our current content
    req.send(JSON.stringify(creds));
}

Added login.py.













































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/python
#
# login.py
#	Verify user/pass
#
import cgi, sys, json, time
import sqlite3
from config import DBNAME
w = sys.stdout.write

# Always a JSON response (even error)
w("Content-Type: application/json\n")

# Connect to DB
# (Use a name which outsiders won't know.)
conn = sqlite3.connect("var/" + DBNAME)

# Say we can't grok it & bail
def fail(msg):
    w("Status: 400 Bad Request\n\n%s" % (json.dumps({"err": msg}),))
    sys.exit(0)

def run():
    # Parse CGI header
    cgi.maxlen = 128*1024
    fields = cgi.parse()

    # JSON body
    try:
	req = json.load(sys.stdin)
	user = str(req["user"])
	pw = str(req["password"])
    except:
	fail("Bad JSON input")

    # Logged in?
    if (not user) or (not pw):
	fail("Bad login credentials")

    # Verify pw
    c = conn.cursor()
    c.execute("select password from accounts where user=?", (user,))
    tup = c.fetchone()
    c.close()
    if (tup is None) or (tup[0] != pw):
	fail("Bad login user '%s' pass '%s'" % (user, pw))

    # Send back success
    w("Status: 200 OK\n\n%s" % (json.dumps({"err": None}),))

    sys.exit(0)

if __name__ == "__main__":
    run()

Changes to sync.py.

1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
17
18
19
..
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
...
102
103
104
105
106
107
108
109
110
111
#!/usr/bin/python
#
# sync.py
#	Accept POST of new entries to our note DB
#
import cgi, sys, json, time
import sqlite3

w = sys.stdout.write

# Connect to DB
# (Use a name which outsiders won't know.)
conn = sqlite3.connect("notes-4323zz.db")

# This is a JSON API
w("Content-Type: application/json\n")

# Say we can't grok it & bail
tm1 = hours1 = tm2 = sel = None
def fail(msg):
................................................................................
# Gather our current notes, keyed by note's index name (noteXXXX)
onotes = {}
c = conn.cursor()
c.execute(
 "select key,tm,title,content from notes where user=? and tm>?",
 (user, tm))
for tup in c:
    onotes[tup[0]] = {"tm": tup[1], "title": tup[2], "content": tup[3])

# For each note which they've changed, either accept the change,
#  or send back the newer content which we've already received.
resp = {}
changes = False
c = conn.cursor()
for key,note in req.iteritems():
................................................................................
# Commit DB changes
if changes:
    conn.commit()
conn.close()

# Give them an update including time
resp["tm"] = time.time()
w(json.dumps(resp))

sys.exit(0)







>




|







 







|







 







|


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
...
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/python
#
# sync.py
#	Accept POST of new entries to our note DB
#
import cgi, sys, json, time
import sqlite3
from config import DBNAME
w = sys.stdout.write

# Connect to DB
# (Use a name which outsiders won't know.)
conn = sqlite3.connect("var/" + DBNAME)

# This is a JSON API
w("Content-Type: application/json\n")

# Say we can't grok it & bail
tm1 = hours1 = tm2 = sel = None
def fail(msg):
................................................................................
# Gather our current notes, keyed by note's index name (noteXXXX)
onotes = {}
c = conn.cursor()
c.execute(
 "select key,tm,title,content from notes where user=? and tm>?",
 (user, tm))
for tup in c:
    onotes[tup[0]] = {"tm": tup[1], "title": tup[2], "content": tup[3]}

# For each note which they've changed, either accept the change,
#  or send back the newer content which we've already received.
resp = {}
changes = False
c = conn.cursor()
for key,note in req.iteritems():
................................................................................
# Commit DB changes
if changes:
    conn.commit()
conn.close()

# Give them an update including time
resp["tm"] = time.time()
w("Status: 200 OK\n\n%s" % (json.dumps(resp),))

sys.exit(0)