1
0
mirror of https://github.com/lxsang/antd-web-apps synced 2025-07-16 13:59:52 +02:00

Compare commits

..

38 Commits

Author SHA1 Message Date
bed7fc6ec0 Merge pull request #38 from lxsang/master
Update style.css
2021-01-03 22:53:03 +01:00
a361a475c9 Merge pull request #37 from lxsang/master
setting file should not be readable by other user
2021-01-02 13:51:55 +01:00
dd0f7bc860 Merge pull request #36 from lxsang/master
update
2020-12-25 22:42:20 +01:00
71a661bc4a Merge pull request #35 from lxsang/master
update script
2020-12-18 21:57:22 +01:00
291a45cdb5 Merge pull request #34 from lxsang/master
fix scrollbug
2020-12-09 12:13:41 +01:00
4ba7bd6b24 Merge pull request #33 from lxsang/master
fix scrolldown bug
2020-12-09 12:01:06 +01:00
47eab0a1dc Merge pull request #32 from lxsang/master
fix scrolldown bug
2020-12-09 11:55:03 +01:00
be1f9e9fe1 Merge pull request #31 from lxsang/master
fix display issues
2020-12-09 11:30:25 +01:00
c076bd68b2 Merge pull request #30 from lxsang/master
fix display issues
2020-12-09 11:27:58 +01:00
185f676dd6 Merge pull request #29 from lxsang/master
Update sites styles
2020-12-09 11:25:11 +01:00
7f326baad5 Merge pull request #28 from lxsang/master
update
2020-11-19 19:03:46 +01:00
91f15fd884 Merge pull request #27 from lxsang/master
add script
2020-11-19 18:10:28 +01:00
cd30af6da4 Merge pull request #26 from lxsang/master
fix local image rendering bug
2020-10-01 11:19:53 +02:00
05ee884e8d Merge pull request #25 from lxsang/master
clean up front pages
2020-09-24 19:19:25 +02:00
d96a9c699e Merge pull request #24 from lxsang/master
support author notification
2020-09-22 19:51:29 +02:00
fda4a23943 Merge pull request #23 from lxsang/master
add To to sendmail function
2020-09-22 18:24:50 +02:00
acbbe8f0a2 Merge pull request #22 from lxsang/master
support comments in odc
2020-09-22 17:59:22 +02:00
99414311e5 Merge pull request #21 from lxsang/master
allow tab, math and highlight in comments
2020-09-22 17:16:54 +02:00
3f7bc5fa7d Merge pull request #20 from lxsang/master
allow tab, math and highlight in comments
2020-09-22 17:14:25 +02:00
dd3e56fb01 Merge pull request #19 from lxsang/master
enhance error handling
2020-09-22 16:51:56 +02:00
5c5d3504ad Merge pull request #18 from lxsang/master
use quicktalk as comment API
2020-09-22 16:24:45 +02:00
53e2b3b891 Merge pull request #17 from lxsang/master
update profile pic
2020-09-18 14:01:59 +02:00
6bde15ff78 Merge pull request #16 from lxsang/master
fix antos build destination
2020-09-18 13:37:08 +02:00
90b812f35a Merge pull request #15 from lxsang/master
use afx in blog
2020-09-18 13:27:43 +02:00
81f8e1c984 Merge pull request #14 from lxsang/master
Update antos doc
2020-09-15 16:56:24 +02:00
6e6b7930bc Merge pull request #13 from lxsang/master
add Youtube support on doc front end
2020-09-15 11:12:13 +02:00
7d05a51cff Merge pull request #12 from lxsang/master
Youtube support for doc frontend
2020-09-15 10:30:57 +02:00
f03d18499b Merge pull request #11 from lxsang/master
clean up
2020-09-13 17:58:07 +02:00
8de304f2c6 Merge pull request #10 from lxsang/master
add jarvis controller
2020-09-13 17:28:32 +02:00
f684005fe4 Merge pull request #9 from lxsang/master
support for local media
2020-09-13 17:16:01 +02:00
257f1e42bd Merge pull request #8 from lxsang/master
fix bug
2020-09-01 18:03:51 +02:00
db730f7220 Merge pull request #7 from lxsang/master
use backend markdown renderer
2020-09-01 17:40:03 +02:00
f04a756b4c Merge pull request #6 from lxsang/master
increase document font size
2020-06-26 16:51:36 +02:00
396f1950dc Merge pull request #5 from lxsang/master
external link should be opened in a new tab
2020-06-26 16:46:25 +02:00
2902e95e86 Merge pull request #4 from lxsang/master
add style for line number
2020-06-26 16:38:00 +02:00
3af5abdf87 Merge pull request #3 from lxsang/master
add style for line number
2020-06-26 16:35:54 +02:00
7e96479136 Merge pull request #2 from lxsang/master
fix auth bug
2020-06-26 15:47:07 +02:00
36c73c8035 Merge pull request #1 from lxsang/master
Add auto build script on pull request
2020-06-26 10:34:20 +02:00
44 changed files with 277 additions and 1576 deletions

View File

@ -1,22 +0,0 @@
---
kind: pipeline
type: exec
name: default
platform:
os: linux
arch: amd64
clone:
disable: true
steps:
- name: clone
commands:
- pwd
- git clone ssh://git@iohub.dev/lxsang/antd-web-apps.git
- cd ./antd-web-apps && git checkout master
- name: build
commands:
- cd ./antd-web-apps
- BUILDDIR=/opt/www/htdocs make
trigger:
branch:
- master

49
Jenkinsfile vendored
View File

@ -1,49 +0,0 @@
def remote = [:]
remote.name = 'workstation'
remote.host = 'workstation'
remote.user = 'dany'
remote.identityFile = '/var/jenkins_home/.ssh/id_rsa'
remote.allowAnyHosts = true
remote.agent = false
remote.logLevel = 'INFO'
pipeline{
agent { node{ label'master' }}
options {
// Limit build history with buildDiscarder option:
// daysToKeepStr: history is only kept up to this many days.
// numToKeepStr: only this many build logs are kept.
// artifactDaysToKeepStr: artifacts are only kept up to this many days.
// artifactNumToKeepStr: only this many builds have their artifacts kept.
buildDiscarder(logRotator(numToKeepStr: "1"))
// Enable timestamps in build log console
timestamps()
// Maximum time to run the whole pipeline before canceling it
timeout(time: 1, unit: 'HOURS')
// Use Jenkins ANSI Color Plugin for log console
ansiColor('xterm')
// Limit build concurrency to 1 per branch
disableConcurrentBuilds()
}
stages
{
stage('Build') {
steps {
sshCommand remote: remote, command: '''
set -e
export WORKSPACE=$(realpath "./jenkins/workspace/antd-web-apps")
cd $WORKSPACE
[ -d build ] && rm -rf build
mkdir -p build/opt/www/htdocs
export BUILDDIR="$WORKSPACE/build/opt/www/htdocs"
make
'''
script {
// only useful for any master branch
//if (env.BRANCH_NAME =~ /^master/) {
archiveArtifacts artifacts: 'build/', fingerprint: true
//}
}
}
}
}
}

View File

@ -1,12 +1,12 @@
BUILDDIR?=./build
PROJS?=grs info blog os doc talk get
PROJS?=grs info blog os doc ci talk get
copyfiles = index.ls mimes.json
main: copy
for f in $(PROJS); do BUILDDIR=$(BUILDDIR)/"$${f}" make -C "$${f}" ; done
copy:
cp -rfv $(copyfiles) $(BUILDDIR)
cp -rv silk $(BUILDDIR)
cp -rf $(copyfiles) $(BUILDDIR)
cp -r silk $(BUILDDIR)
ar:
-[ -d /tmp/antd_web_apps ] && rm -r /tmp/antd_web_apps
@ -19,4 +19,4 @@ ar:
clean:
-for f in $(PROJS); do rm -r $(BUILDDIR)/"$${f}"; done
-for f in $(copyfiles); do rm -r $(BUILDDIR)/"$${f}"; done
-rm -r $(BUILDDIR)/silk
-rm -r $(BUILDDIR)/silk

View File

@ -1,2 +1,2 @@
# antd-web-apps
Various web apps for antd server
Some web apps for antd server

View File

@ -1,4 +1,4 @@
copyfiles = assets views models ai controllers router.lua
copyfiles = assets views models controllers router.lua
main:
- mkdir -p $(BUILDDIR)

View File

@ -1,345 +0,0 @@
local doclassify = {}
local st = require("stmr")
doclassify.bow = function(data, stopwords)
-- first step get a table of worlds that contain
-- world: occurences
local bag = {}
for w in data:gmatch('%w+') do
local word = w:lower()
if not stopwords[word] then
word = st.stmr(word)
if bag[word] then
bag[word].count = bag[word].count + 1
else
bag[word] = {count=0, tf=0, tfidf=0.0}
bag[word].count = 1
end
end
end
-- now calculate the tf of the bag
for k,v in pairs(bag) do
bag[k].tf = math.log(1 + bag[k].count)
end
return bag
end
doclassify.len = function(table)
local cnt = 0
for k,v in pairs(table) do cnt = cnt+1 end
return cnt
end
doclassify.tfidf = function(documents)
-- now for each term in a bag, calculate
-- the inverse document frequency, which
-- is a measure of how much information
-- the word provides, that is, whether the
-- term is common or rare across all documents
local ndoc = doclassify.len(documents)
for k,bag in pairs(documents) do
-- for eacht term in bag
-- calculate its idf across all documents
for term,b in pairs(bag) do
local n = 0
for id,doc in pairs(documents) do
if doc[term] then n = n+1 end
end
--echo("term:"..term.." appears in"..n.." documents")
b.tfidf = b.tf*math.log(ndoc/n)
end
end
end
doclassify.search = function(term, documents)
local r = {}
for id, doc in pairs(documents) do
if doc[term:lower()] then
r[id] = doc[term].tfidf
end
end
return r
end
doclassify.get_vectors = function(documents)
-- get a list of vector from documents
local index = 0
local vectors = {}
local maps = {}
local terms = {}
local maxv = 0
for id in pairs(documents) do
maps[id] = {}
vectors[id] = {}
end
-- first loop, get the term
for id, doc in pairs(documents) do
for k,v in pairs(doc) do
-- get max value
if v.tfidf > maxv then
maxv = v.tfidf
end
-- get the term
if not terms[k] then
index = index + 1
terms[k] = index
end
for pid in pairs(documents) do
if not maps[pid][k] then
if id == pid then
maps[pid][k] = v.tfidf
else
maps[pid][k] = 0
end
else
if maps[pid][k] == 0 and id == pid then
maps[pid][k] = v.tfidf
end
end
end
end
end
-- reindexing the vectors
for id in pairs(documents) do
for k,v in pairs(maps[id]) do
vectors[id][terms[k]] = v
end
end
--echo("Max tfidf "..maxv.." in document #"..maxid.." of term "..term)
return vectors, maxv, index, terms
end
doclassify.similarity = function(va, vb)
-- using cosin similarity
local dotp = 0
local maga = 0
local magb = 0
for k = 1,#va do
dotp = dotp + va[k]*vb[k]
maga = maga + va[k]*va[k]
magb = magb + vb[k]*vb[k]
end
maga = math.sqrt(maga)
magb = math.sqrt(magb)
local d = 0
if maga ~= 0 and magb ~= 0 then
d = dotp/ (magb*maga)
end
return d
end
doclassify.similarities = function(v1, collection)
local similarities = {}
assert(#v1 == #(collection[1]), "Incorrect vectors size")
for i=1,#collection do
similarities[i] = doclassify.similarity(v1, collection[i])
end
return similarities
end
doclassify.mean_similarity = function(v1, v2)
assert(#v1 == #v2, "Incorrect vectors size")
local similarities = {}
for i = 1,#v1 do similarities[i] = doclassify.similarity(v1[i], v2[i]) end
return doclassify.mean(similarities)
end
doclassify.similarity_chart = function(id, vectors)
local vs = {}
local cnt = 0
local lut = {}
for k,v in pairs(vectors) do
if k ~= id then
cnt = cnt + 1
vs[cnt] = v
lut[cnt] = k
end
end
if not vs[1] then return {} end
return doclassify.similarities(vectors[id], vs), lut
end
doclassify.top_similarity = function(id, vectors, n, th)
local chart,lut = doclassify.similarity_chart(id,vectors)
--echo(JSON.encode(chart))
--echo(JSON.encode(lut))
if not lut or #lut <= 0 then return nil end
local top = {}
local j=0
local goon = true
if not th then
goon = false
end
while j < n or goon
do
local i,maxv = doclassify.argmax(chart)
top[lut[i]] = maxv
chart[i] = 0.0
j=j+1
if maxv < th and goon then
goon = false
end
end
--for j=1,n do
-- local i,maxv = doclassify.argmax(chart)
-- top[lut[i]] = maxv
-- chart[i] = 0.0
--end
return top
end
doclassify.save_vectors = function(vectors, name)
local f = io.open(name,"w")
if f == nil then return false end
for id, v in pairs(vectors) do
f:write(id)
for i=1,#v do f:write(","..v[i]) end
f:write("\n")
end
f:close()
return true
end
doclassify.save_topchart = function(vectors, name,n)
local f = io.open(name,"w")
if f == nil then return false end
for k,v in pairs(vectors) do
local top = doclassify.top_similarity(k,vectors,n, 0.1)
for a,b in pairs(top) do
f:write(k.." "..a.." "..b.."\n")
end
end
f:close()
return true
end
doclassify.kmean = function(nclass, documents, maxstep, ids)
-- now
local vectors, maxv, size = doclassify.get_vectors(documents)
-- random centroids
local centroids = {}
local old_centroids = {}
local clusters = {}
--for pid in pairs(documents) do clusters[pid] = 0 end
-- add noise to mean_vector
for i = 1,nclass do
if ids == nil then
centroids[i] = doclassify.random(size,math.floor(maxv))
else
centroids[i] = vectors[ids[i]]
end
old_centroids[i] = doclassify.zeros(size)
end
-- loop until convergence or maxstep reached
local similarity = doclassify.mean_similarity(centroids, old_centroids)
local step = maxstep
while 1.0-similarity > 1e-9 and step > 0 do
clusters = {}
--echo(JSON.encode(centroids))
for id,v in pairs(vectors) do
local similarities = doclassify.similarities(v, centroids)
--echo(JSON.encode(similarities))
local cluster, maxvalue = doclassify.argmax(similarities)
--echo("doc #"..id.." is in clusters #"..cluster.." max value is "..maxvalue)
clusters[id] = cluster
end
-- storing the old centroids
old_centroids = centroids
-- calculate new centroids
local new_centroids = {}
for class in pairs(centroids) do
local cnt = 0
local cvectors = {}
for id,v in pairs(vectors) do
if clusters[id] == class then
cnt = cnt + 1
cvectors[cnt] = v
end
end
new_centroids[class] = doclassify.mean_vector(cvectors, size)
end
centroids = new_centroids
--echo(JSON.encode(centroids))
--echo(JSON.encode(old_centroids))
similarity = doclassify.mean_similarity(centroids, old_centroids)
echo("step #"..step..", similarity "..similarity)
step = step - 1
end
local results = {}
for i = 1,nclass do
local list = {}
local cnt = 0
for id,c in pairs(clusters) do
if c == i then
cnt = cnt + 1
list[cnt] = id
end
end
results[i] = list
end
return results, clusters, centroids
end
doclassify.zeros = function(n)
local vector = {}
for i = 1,n do vector[i] = 0.0 end
return vector
end
doclassify.random = function(n,maxv)
local vector = {}
for i=1,n do
vector[i] = math.random() + math.random(0, maxv)
end
return vector
end
doclassify.sum = function(v)
local sum = 0.0
for i=1,#v do sum = sum + v[i] end
return sum
end
doclassify.mean = function(v)
return doclassify.sum(v)/#v
end
doclassify.mean_vector = function(vectors, size)
local means = doclassify.zeros(size)
if not vectors or #vectors == 0 then return means end
--local size = #(vectors[1])
local times = 0
for k,v in pairs(vectors) do
for i=1,#v do means[i] = means[i] + v[i] end
times = times + 1
end
for i = 1,size do means[i] = means[i]/times end
return means
end
doclassify.argmin = function(v)
local minv = 0.0
local mini = 0.0
for i = 1,#v do
if v[i] <= minv then
mini = i
minv = v[i]
end
end
--echo("min index"..mini.." val "..minv)
return mini, minv
end
doclassify.argmax = function(v)
local maxv = 0.0
local maxi = 0.0
for i = 1,#v do
if v[i] >= maxv then
maxi = i
maxv = v[i]
end
end
return maxi,maxv
end
return doclassify

View File

@ -1,29 +0,0 @@
local gettext = {}
require("sqlite")
gettext.get = function(q)
local db = require("os.libs.dbmodel").get("mrsang","blogs",nil)
if not db then return nil end
local exp = {["="] =q}
local cond = {
exp = exp,
fields = {"id", "content"}
}
local data, sort = db:find(cond)
db:close()
if not data or #data == 0 then return nil end
--for k,v in pairs(data) do
-- data[k].content = bytes.__tostring(std.b64decode(data[k].content)):gsub("%%","%%%%")
--end
return data
end
gettext.stopwords = function(ospath)
--local ospath = require("fs/vfs").ospath(path)
local words = {}
for line in io.lines(ospath) do
words[line] = true
end
return words
end
return gettext

View File

@ -1,151 +0,0 @@
i
me
my
myself
we
our
ours
ourselves
you
your
yours
yourself
yourselves
he
him
his
himself
she
her
hers
herself
it
its
itself
they
them
their
theirs
themselves
what
which
who
whom
this
that
these
those
am
is
are
was
were
be
been
being
have
has
had
having
do
does
did
doing
a
an
the
and
but
if
or
because
as
until
while
of
at
by
for
with
about
against
between
into
through
during
before
after
above
below
to
from
up
down
in
out
on
off
over
under
again
further
then
once
here
there
when
where
why
how
all
any
both
each
few
more
most
other
some
such
no
nor
not
only
own
same
so
than
too
very
s
t
can
will
just
don
should
now
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
w
r
s
t
x
y
z

View File

@ -1,50 +0,0 @@
local path = require("fs/vfs").ospath("home://aiws/blog-clustering")
local gettext = loadfile(path.."/gettext.lua")()
local cluster = loadfile(path.."/cluster.lua")()
local refresh = false
local file = "/home/mrsang/test.csv"
if refresh then
local data = gettext.get({publish=1})
local documents = {}
if data then
local sw = gettext.stopwords("home://aiws/blog-clustering/stopwords.txt")
for k,v in pairs(data) do
local bag = cluster.bow(data[k].content, sw)
documents[data[k].id] = bag
end
cluster.tfidf(documents)
--local v = cluster.search("arm", documents)
--echo(JSON.encode(v))
local vectors, maxv, size = cluster.get_vectors(documents)
local s = cluster.save_topchart(vectors,file, 3)
if s then echo("file saved") else echo("error save file") end
--echo(JSON.encode(r))
--r = cluster.similarity(vectors["14"],vectors["16"])
--echo("Similarity "..r)
--local c,l = cluster.kmean(3, documents, 10)
--echo(JSON.encode(c))
--echo(JSON.encode(l))
else
echo("Data missing")
end
else
local f = io.open(file,"r")
local result = {}
for line in f:lines() do
local arr = {}
local cnt = 0
for i in line:gmatch( "%S+") do
cnt = cnt + 1
arr[cnt] = i
end
if not result[arr[1]] then result[arr[1]] = {} end
result[arr[1]][arr[2]] = tonumber(arr[3])
end
f:close()
echo(JSON.encode(result))
--local r = cluster.top_similarity("2",vectors, 3)
--echo(JSON.encode(r))
end

View File

@ -1,220 +0,0 @@
$(document).ready(function () {
const colors = [
"#3957ff", "#d3fe14", "#c9080a", "#fec7f8", "#0b7b3e", "#0bf0e9", "#c203c8", "#fd9b39",
"#888593", "#906407", "#98ba7f", "#fe6794", "#10b0ff", "#ac7bff", "#fee7c0", "#964c63",
"#1da49c", "#0ad811", "#bbd9fd", "#fe6cfe", "#297192", "#d1a09c", "#78579e", "#81ffad",
"#739400", "#ca6949", "#d9bf01", "#646a58", "#d5097e", "#bb73a9", "#ccf6e9", "#9cb4b6",
"#b6a7d4", "#9e8c62", "#6e83c8", "#01af64", "#a71afd", "#cfe589", "#d4ccd1", "#fd4109",
"#bf8f0e", "#2f786e", "#4ed1a5", "#d8bb7d", "#a54509", "#6a9276", "#a4777a", "#fc12c9",
"#606f15", "#3cc4d9", "#f31c4e", "#73616f", "#f097c6", "#fc8772", "#92a6fe", "#875b44",
"#699ab3", "#94bc19", "#7d5bf0", "#d24dfe", "#c85b74", "#68ff57", "#b62347", "#994b91",
"#646b8c", "#977ab4", "#d694fd", "#c4d5b5", "#fdc4bd", "#1cae05", "#7bd972", "#e9700a",
"#d08f5d", "#8bb9e1", "#fde945", "#a29d98", "#1682fb", "#9ad9e0", "#d6cafe", "#8d8328",
"#b091a7", "#647579", "#1f8d11", "#e7eafd", "#b9660b", "#a4a644", "#fec24c", "#b1168c",
"#188cc1", "#7ab297", "#4468ae", "#c949a6", "#d48295", "#eb6dc2", "#d5b0cb", "#ff9ffb",
"#fdb082", "#af4d44", "#a759c4", "#a9e03a", "#0d906b", "#9ee3bd", "#5b8846", "#0d8995",
"#f25c58", "#70ae4f", "#847f74", "#9094bb", "#ffe2f1", "#a67149", "#936c8e", "#d04907",
"#c3b8a6", "#cef8c4", "#7a9293", "#fda2ab", "#2ef6c5", "#807242", "#cb94cc", "#b6bdd0",
"#b5c75d", "#fde189", "#b7ff80", "#fa2d8e", "#839a5f", "#28c2b5", "#e5e9e1", "#bc79d8",
"#7ed8fe", "#9f20c3", "#4f7a5b", "#f511fd", "#09c959", "#bcd0ce", "#8685fd", "#98fcff",
"#afbff9", "#6d69b4", "#5f99fd", "#aaa87e", "#b59dfb", "#5d809d", "#d9a742", "#ac5c86",
"#9468d5", "#a4a2b2", "#b1376e", "#d43f3d", "#05a9d1", "#c38375", "#24b58e", "#6eabaf"];
d3.json("/post/graph_json")
.then(
function (json) {
if (json.result) {
const tooltip_div = d3.select("#desktop")
.append("div")
.attr("class", "d3tooltip")
.style("display", "none");
const links = json.result.links;
const nodes = json.result.nodes;
drag = simulation => {
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
tooltip_div.style("display", "none");
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
};
const simulation = d3.forceSimulation(nodes)
.force("link",
d3.forceLink(links)
.id(d => d.id)
.distance(d => 1.0 / d.score)
.strength(d => d.score)
)
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter());
const svg = d3.create("svg")
.attr("preserveAspectRatio", "xMidYMid meet");
const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.8)
.selectAll("line")
.data(links)
.join("line")
.attr("stroke-width", d => d.score * 7.0); //d.score
const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 0.5)
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", (d) => {
conn = links.filter((l) => {
//console.log(d.id, l.target.id, l.source.id)
return l.target.id == d.id || l.source.id == d.id;
}).map(c=>c.score);
//return conn.reduce((a, b) => a + b, 0) * 10;
return conn.length;
})
.attr("fill", (d) => {
conn = links.filter((l) => {
//console.log(d.id, l.target.id, l.source.id)
return l.target.id == d.id || l.source.id == d.id;
});
return colors[conn.length % colors.length - 1];
})
.on("click", (d) => {
const index = $(d.target).index();
const data = nodes[index];
d3.json("/post/json/" + data.id)
.then( (json) => {
if(json.result)
{
$("#floating_content").html(json.result.description);
$("#floating_container").show();
$("#floating_btn_read_more").attr("href", "/post/id/" + json.result.id);
}
})
.catch ((e)=>{
console.log(e);
});
})
.call(drag(simulation))
.on('mouseover', function (d) {
const index = $(d.target).index();
const data = nodes[index];
link.style('stroke', function (l) {
if (data.id == l.source.id || data.id == l.target.id)
return "#9a031e";
else
return "#999";
});
const off = $("#desktop").offset();
tooltip_div.transition()
.duration(200)
tooltip_div.style("display", "block")
.style("opacity", .8);
tooltip_div.html(data.title)
.style("left", (d.clientX - off.left + 10) + "px")
.style("top", (d.clientY - off.top + 10) + "px");
})
.on('mouseout', function () {
link.style('stroke', "#999");
tooltip_div.style("display", "none");
});
//node.append("title")
// .text(d => d.title);
/*const label = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 0.2)
.selectAll("text")
.data(nodes)
.join("text")
.text(d=>d.id)
.style("user-select", "none")
.style("font-size", (d) =>{
conn = links.filter((l) => {
//console.log(d.id, l.target.id, l.source.id)
return l.target.id == d.id || l.source.id == d.id;
});
return conn.length + "px";
})
.style('fill', '#000');*/
simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
const nodes_x = nodes.map(d => d.x);
const nodes_y = nodes.map(d => d.y);
const min_x = Math.min(...nodes_x) - 10;
const min_y = Math.min(...nodes_y) - 10;
const w = Math.max(...nodes_x) - min_x + 10;
const h = Math.max(...nodes_y) - min_y + 10;
svg.attr("viewBox",
[min_x, min_y, w, h]);
/*label
.attr("x", d => {
conn = links.filter((l) => {
//console.log(d.id, l.target.id, l.source.id)
return l.target.id == d.id || l.source.id == d.id;
});
return d.x - conn.length / 2;
})
.attr("y", d => {
conn = links.filter((l) => {
//console.log(d.id, l.target.id, l.source.id)
return l.target.id == d.id || l.source.id == d.id;
});
return d.y + conn.length / 2;
});*/
});
// invalidation.then(() => simulation.stop());
$("#floating_btn_close").click((e)=>{
$("#floating_container").hide();
});
$("#desktop")
.css("position", "relative");
$("#container")
.css("height", "100%")
.css("position", "relative")
.append($(svg.node())
.css("height", "calc(100% - 10px)")
.css("margin", "0 auto")
.css("display", "block"));
$("#floating_container").show();
}
}
)
.catch((e) => {
console.log(e);
});
});

View File

@ -18,6 +18,7 @@ function subscribe(prefix) {
scheme = undefined;
});
obs.on("rendered", function (d) {
console.log("rednered");
$(".afx-window-title", scheme).html("Subscribe");
$("[data-id='send']", scheme).click(function () {
var status = $("[data-id='status']", scheme);

View File

@ -73,52 +73,6 @@ body {
justify-content: flex-end;
flex-direction: row;
}
div.d3tooltip {
position: absolute;
padding: 5px;
background-color: #2c2c2c;
color: white;
border: 0px;
border-radius: 5px;
max-width: 300px;
overflow: auto;
}
div.d3tooltip a{
color: white;
text-decoration: underline;
}
#floating_container {
position: absolute;
max-width: 50%;
height: calc(100% - 1px);
background-color: #2c2c2c;
opacity: 0.9;
right: 0;
overflow:hidden;
display: none;
}
#floating_content
{
overflow-y:auto;
height: 100%;
padding-left: 15px;
padding-right: 15px;
}
#floating_btn_container
{
display: block;
height: 24px;
padding: 5px;
font-weight: bold;
border-bottom: 1px solid #cccccc;
}
#floating_btn_container a {
text-decoration: none;
margin-right: 10px;
}
#floating_container, #floating_container a{
color: white;
}
#navbar.navmobile {
margin: 0 auto;
max-width: 960px;

View File

@ -88,43 +88,6 @@ function PostController:bytag(b64tag, limit, action, id)
return true
end
function PostController:json(id)
local obj = {
error = false,
result = false
}
local data, order = self.blog:fetch({["="] = {id = id}})
if not data or #order == 0 then
obj.error = "No data found"
else
data = data[1]
obj.result = {
id = data.id,
title = data.title,
description = nil,
tags = data.tags,
ctime = data.ctimestr,
utime = data.utimestr
}
local c, d = data.content:find("%-%-%-%-%-")
if c then
obj.description = data.content:sub(0, c - 1)
else
obj.description = data.content
end
-- convert description to html
local content = ""
local md = require("md")
local callback = function(s) content = content .. s end
md.to_html(obj.description, callback)
obj.result.description = content
end
std.json()
std.t(JSON.encode(obj));
return false;
end
function PostController:id(pid)
local data, order = self.blog:fetch({["="] = {id = pid}})
if not data or #order == 0 then
@ -167,57 +130,11 @@ function PostController:actionnotfound(...)
return self:notfound("Action [" .. args[1] .. "] not found")
end
function PostController:graph_json(...)
local nodes = self.blog:find({exp= { ["="] = { publish = 1}}, fields = {"id", "title"}})
local output = { error = false, result = false }
local lut = {}
std.json()
if not nodes then
output.error = "No nodes found"
else
output.result = {
nodes = {},
links = {}
}
for k,v in ipairs(nodes) do
local title = v.title
output.result.nodes[k] = { id = tonumber(v.id), title = title }
end
-- get statistic links
local links = self.analytical:find({fields = {"pid", "sid", "score"}})
if links then
local i = 1
for k,v in ipairs(links) do
local link = { source = tonumber(v.pid), target = tonumber(v.sid), score = tonumber(v.score)}
local key = ""
if link.source < link.target then
key = v.pid..v.sid
else
key = v.sid..v.pid
end
key = std.sha1(key)
if not lut[key] then
output.result.links[i] = link
i = i + 1
lut[key] = true
end
end
end
end
std.t(JSON.encode(output))
return false
end
function PostController:graph(...)
self.template:set("title", "Posts connection graph")
self.template:set("d3", true)
return true
end
function PostController:analyse(n)
if not n then
n = 5
end
local path = WWW_ROOT..DIR_SEP.."ai"
local path = "/home/mrsang/aiws/blog-clustering"
local gettext = loadfile(path .. "/gettext.lua")()
local cluster = loadfile(path .. "/cluster.lua")()
local data = gettext.get({publish = 1})
@ -236,7 +153,7 @@ function PostController:analyse(n)
self.analytical:delete({["="] = {["1"] = 1}})
-- get similarity and put to the table
for id, v in pairs(vectors) do
local top = cluster.top_similarity(id, vectors, tonumber(n), 0.1)
local top = cluster.top_similarity(id, vectors, n)
for a, b in pairs(top) do
local record = {pid = id, sid = a, score = b}
self.analytical:create(record)

View File

@ -27,7 +27,7 @@ function BlogModel:fetch(cnd, limit, order)
exp = {["and"] = exp },
order = { ctime = "DESC" },
fields = {
"id", "title", "utime", "ctime", "utimestr", "content", "ctimestr", "rendered", "tags"
"id", "title", "utime", "ctime", "utimestr", "ctimestr", "rendered", "tags"
}
}
if limit then

View File

@ -5,7 +5,6 @@
local render = __main__:get("render")
local url = __main__:get("url")
local tags = __main__:get("tags")
local d3 = __main__:get("d3")
local cls = ""
if HEADER.mobile then
cls = "navmobile"
@ -28,16 +27,8 @@
<script src="https://chat.iohub.dev/assets/quicktalk.js"> </script>
<script src="<?=HTTP_ROOT?>/rst/afx.js"> </script>
<script src="<?=HTTP_ROOT?>/rst/gscripts/jquery-3.4.1.min.js"> </script>
<script src="<?=HTTP_ROOT?>/rst/gscripts/jquery-3.2.1.min.js"> </script>
<script src="<?=HTTP_ROOT?>/assets/main.js"></script>
<?lua if d3 then ?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.5.0/d3.min.js" ></script>
<script src="https://d3js.org/d3-dispatch.v2.min.js"></script>
<script src="https://d3js.org/d3-quadtree.v2.min.js"></script>
<script src="https://d3js.org/d3-timer.v2.min.js"></script>
<script src="https://d3js.org/d3-force.v2.min.js"></script>
<script src="<?=HTTP_ROOT?>/assets/graph.js"></script>
<?lua end ?>
<meta property="og:image" content="" />
<?lua if render then ?>
<meta name="twitter:card" content="summary" />
@ -125,14 +116,14 @@
<div class = "logo"><a href = "https://lxsang.me"></a></div>
<ul>
<li><i class = "fa fa-home"></i><a href="<?=HTTP_ROOT?>">Home</a></li>
<li ><i class = "fa fa-address-card"></i><a href="https://info.lxsang.me" >Portfolio</a></li>
<li><i class = "fa fa-envelope"></i><a href="#" onclick="mailtoMe('<?=HTTP_ROOT?>')" >Contact</a></li>
<?lua
if not HEADER.mobile then
?>
<li > <i class = "fa fa-globe"></i><a href = "/post/graph">Explore</a></li>
<li> <i class = "fa fa-paper-plane"></i><a href="#" onclick="subscribe('<?=HTTP_ROOT?>')">Subscribe</a></li>
<li > <i class = "fa fa-globe"></i><a href = "https://os.lxsang.me" target="_blank">AntOS</a></li>
<?lua end ?>
<li ><i class = "fa fa-address-card"></i><a href="https://info.lxsang.me" >Portfolio</a></li>
<li><i class = "fa fa-envelope"></i><a href="#" onclick="mailtoMe('<?=HTTP_ROOT?>')" >Contact</a></li>
</ul>
<?lua
if not HEADER.mobile then
@ -152,7 +143,7 @@
</div>
</div>
<div id = "bottom">
Powered by antd server, (c) 2017 - <?=os.date("*t").year?> Dany LE
Powered by antd server, (c) 2017 - <?=os.date("*t").year?> Xuan Sang LE
</div>
</body>
</html>
</html>

View File

@ -1,26 +0,0 @@
<div id="floating_container">
<div id="floating_btn_container">
<a id="floating_btn_close" href="#" ><i class="fa fa-close"></i>&nbsp;Close</a>
<a id="floating_btn_read_more" href="#"><i class="fa fa-chain"></i>&nbsp;Read more</a>
</div>
<div id="floating_content">
<p>
The graph shows this blog posts relationship in term of similarity.
Each node in the graph is a post, two nodes are connected by an edge if
they share some degree of similarity (weighted by edge thickness and edge distance).
A large edge thickness and/or short edge distance shows a strong similarity between
the two connected nodes.
</p>
<p>
Nodes are arranged by force which is modelled by content similarity.
The more similar the nodes content, the stronger the force between them.
Therefore, nodes that share similar topic will tend to group themself together in a cluster.
</p>
<p>
Navigate the blog by hovering the mouse on a node and following the node relationship
(edges) to find your interesting topic.
</p>
</div>
</div>

View File

@ -1,6 +1,3 @@
<?lua if not HEADER.mobile then ?>
<iframe width="980" height="410" src="https://mars.nasa.gov/layout/embed/send-your-name/future/certificate/?cn=792789419260" frameborder="0"></iframe>
<?lua end ?>
<?lua
local datas = posts
local class = "card"

6
ci/Makefile Normal file
View File

@ -0,0 +1,6 @@
copyfiles = router.lua scripts
main:
- mkdir -p $(BUILDDIR)
cp -rvf $(copyfiles) $(BUILDDIR)
- mkdir -p $(BUILDDIR)/log

82
ci/router.lua Normal file
View File

@ -0,0 +1,82 @@
-- the rewrite rule for the framework
-- should be something like this
-- ^\/apps\/+(.*)$ = /apps/router.lua?r=<1>&<query>
-- some global variables
function fail(msg)
std.json()
std.t(JSON.encode({error=msg}))
end
function result(obj)
std.json()
std.t(JSON.encode({result=obj, error=false}))
end
DIR_SEP = "/"
WWW_ROOT = __ROOT__.."/ci"
if HEADER.Host then
HTTP_ROOT= "https://"..HEADER.Host
else
HTTP_ROOT = "https://ci.iohub.dev"
end
-- class path: path.to.class
BASE_FRW = ""
-- class path: path.to.class
CONTROLLER_ROOT = BASE_FRW.."ci.controllers"
MODEL_ROOT = BASE_FRW.."ci.models"
-- file path: path/to/file
VIEW_ROOT = WWW_ROOT..DIR_SEP.."views"
LOG_ROOT = WWW_ROOT..DIR_SEP.."logs"
-- require needed library
require(BASE_FRW.."silk.api")
function NotfoundController:index(...)
local args = {...}
if #args == 0 then
fail("Unknown action")
return false
end
local action = args[1]
if action == "BuildController" then
if REQUEST.json then
local request = JSON.decodeString(REQUEST.json)
if request.ref and request.ref == "refs/heads/ci" then
local branch = "ci"
local repository = request.repository.name
local path = WWW_ROOT..DIR_SEP.."scripts"..DIR_SEP..repository..".sh"
if ulib.exists(path) then
result("Build action triggered, log file will soon be available at: https://ci.iohub.dev/log/"..repository.."_"..branch..".txt")
os.execute("at now -f "..path)
else
fail("No build script found")
end
else
result("This action is ignored by the CI")
end
else
fail("Unknow action parameters")
end
else
fail("Action not supported: "..action)
end
end
-- registry object store global variables
local REGISTRY = {}
-- set logging level
REGISTRY.logger = Logger:new{ levels = {INFO = false, ERROR = false, DEBUG = false}}
REGISTRY.layout = 'default'
REGISTRY.fileaccess = true
local router = Router:new{registry = REGISTRY}
REGISTRY.router = router
router:setPath(CONTROLLER_ROOT)
--router:route('edit', 'post/edit', "ALL" )
router:route('default', default_routes_dependencies )
router:delegate()

View File

@ -0,0 +1,31 @@
#! /bin/bash
BRANCH="ci"
PRJ="antd-web-apps"
DEST="/opt/www/htdocs"
REPO="https://github.com/lxsang/$PRJ.git"
if [ ! -z $1 ]; then
BRANCH="$1"
fi
{
echo "Build date: $(date)"
echo "Building $PRJ using branch $BRANCH..."
if [ -d "/tmp/ci/$PRJ" ]; then
echo "Clean up /tmp/ci/$PRJ"
rm -rf /tmp/ci/$PRJ
else
echo "Creating /tmp/ci/"
mkdir -p "/tmp/ci"
fi
cd /tmp/ci || (echo "Unable to change directory to /tmp/ci" && exit 1)
echo "Cloning $PRJ (branch $BRANCH) to /tmp/ci..."
git clone -b "$BRANCH" --single-branch --depth=1 "$REPO"
cd "$PRJ" || (echo "Unable to change directory to source code folder" && exit 1)
mkdir -p "$DEST"
BUILDDIR="$DEST" make
echo "Done!"
} 2>&1 | tee "/opt/www/htdocs/ci/log/${PRJ}_${BRANCH}.txt"

31
ci/scripts/antos.sh Normal file
View File

@ -0,0 +1,31 @@
#! /bin/bash
BRANCH="ci"
PRJ="antos"
DEST="/opt/www/htdocs/"
# /opt/www/htdocs
REPO="https://github.com/lxsang/$PRJ.git"
if [ ! -z $1 ]; then
BRANCH="$1"
fi
{
echo "Build date: $(date)"
echo "Building $PRJ using branch $BRANCH..."
if [ -d "/tmp/ci/$PRJ" ]; then
echo "Clean up /tmp/ci/$PRJ"
rm -rf /tmp/ci/$PRJ
else
echo "Creating /tmp/ci/"
mkdir -p "/tmp/ci"
fi
cd /tmp/ci || (echo "Unable to change directory to /tmp/ci" && exit 1)
echo "Cloning $PRJ (branch $BRANCH) to /tmp/ci..."
git clone -b "$BRANCH" --single-branch --depth=1 "$REPO"
cd "$PRJ" || (echo "Unable to change directory to source code folder" && exit 1)
npm i @types/jquery
mkdir -p "$DEST/os"
BUILDDIR="$DEST/os" make release
mkdir -p "$DEST/grs"
BUILDDIR="$DEST/grs" make standalone_tags
echo "Done!"
} 2>&1 | tee "/opt/www/htdocs/ci/log/${PRJ}_${BRANCH}.txt"

Binary file not shown.

View File

@ -1,4 +0,0 @@
html, body {
margin: 0;
height: 100%;
}

View File

@ -29,7 +29,6 @@ body {
width: 100%;
padding: 5px;
border-top: 1px solid #878887;
z-index: 22;
}
#cover {
@ -39,14 +38,14 @@ body {
}
#navbar {
margin: 0 auto;
/* max-width: 80%; */
max-width: 80%;
display: flex;
justify-content: flex-end;
flex-direction: row;
}
#book {
margin: 0 auto;
/* max-width: 80%; */
max-width: 80%;
max-height: 100%;
display: block;
justify-content: flex-end;
@ -54,36 +53,10 @@ body {
text-align: justify;
height: 100%;
}
div.doc-toc-menu {
div.doc-name {
text-align: left;
padding-top: 3px;
}
div.doc-toc-menu a {
text-decoration: none;
color: #c9c9c9;
}
div.doc-toc-legend::before
{
content: "\f0c9";
color: #2c2c2c;
width: 20px;
height: 25px;
margin-right: 3px;
font-family: "FontAwesome";
font-size: 18px;
}
div.doc-toc-menu a::before {
/* padding-top:13px; */
content: "\f02d";
color: #c9c9c9;
width: 20px;
height: 25px;
font-family: "FontAwesome";
font-size: 18px;
}
div.doc-name{
font-weight: bold;
}
div.doc-name a {
text-decoration: none;
color: #c9c9c9;
@ -92,14 +65,13 @@ div.doc-name a {
a.x-link,
a.x-link:hover {
text-decoration: none;
color: #2c2c2c;
font-weight: bold;
/* padding-left: 15px; */
color: #c9c9c9;
padding-left: 15px;
padding-top: 7px;
}
a.x-link::before {
content: "\f08e";
color: #2c2c2c;
color: #c9c9c9;
width: 20px;
height: 25px;
font-family: "FontAwesome";
@ -108,7 +80,7 @@ a.x-link::before {
div.doc-name a::before {
/* padding-top:13px; */
content: "\f015";
color: #2c2c2c;
color: #c9c9c9;
width: 20px;
height: 25px;
font-family: "FontAwesome";
@ -145,8 +117,8 @@ div.search-icon {
width: 35px;
}
div.doc-toc {
max-width: 70%;
padding-right: 0;
width: 300px;
/* font-size: 11px; */
background-color: #e3e3e3;
color: #2c2c2c;
overflow: auto;
@ -154,38 +126,8 @@ div.doc-toc {
top: 30px;
bottom: 30px;
border-right: 1px solid #c9c9c9;
border-left: 1px solid #c9c9c9;
padding-top: 0;
padding-bottom: 10px;
box-shadow: 0px 6px 3px -1px rgba(0,0,0,0.65);
z-index: 20;
padding-top: 10px;
}
a.toc-active {
border: 1px solid #c9c9c9;
border-bottom: 0;
background-color: #e3e3e3;
outline: none;
display: block;
padding-left: 5px;
padding-right: 5px;
color: #2c2c2c !important;
}
a.toc-active::before{
color: #2c2c2c !important;
}
div.doc-toc-header{
display: block;
padding: 0px;
margin: 0px;
padding-left: 10px;
padding: 3px;
/* height: 24px; */
border-bottom: 1px solid #c9c9c9;
font-weight: bold;
}
div.doc-toc a {
text-decoration: none;
color: #2c2c2c;
@ -195,7 +137,6 @@ div.doc-toc a {
list-style-type: none;
padding: 0;
padding-left: 10px;
padding-right: 10px;
}
div.doc-toc ul.nested {
list-style-type: none;
@ -237,7 +178,7 @@ div.doc-toc a.highlight {
div.doc-content {
display: block;
width: 100%;
width: calc(100% - 300px);
float: right;
padding-left: 10px;
}

View File

@ -1,91 +0,0 @@
BaseController:subclass(
"OfficeController",
{
registry = {},
models = {}
}
)
local docType = function(ext)
if ext == "doc" or ext=="docx" or ext =="odt" then
return "word"
elseif ext == "csv" or ext =="ods" or ext== "xls" or ext == "xlsx" then
return "cell"
elseif ext == "odp" or ext == "ppt" or ext == "pptx" then
return "slide"
else
return "none"
end
end
function OfficeController:index(sid)
-- doing nothing here
require("sqlite")
local ospath = require("shared").ospath(sid)
local ext = ospath:match("^.+%.(.+)$")
local name = ospath:match("^.+/(.+)$")
if(ulib.exists(ospath)) then
local stat = ulib.file_stat(ospath)
if stat.error == nil then
local key = std.sha1(ospath..":"..stat.mtime)
self.template:set("shareid", sid)
self.template:set("ext", ext )
self.template:set("doctype", docType(ext) )
self.template:set("name", name)
self.template:set("key", key)
end
end
self:switchLayout("office")
return true
end
function OfficeController:shared(id)
require("sqlite")
require(BASE_FRW.."shared").get(id)
return false
end
function OfficeController:save(sid)
require("sqlite")
local ospath = require("shared").ospath(sid)
std.json()
local obj = {
error = false,
result = false
}
if not REQUEST.json then
obj.error = "Invalid request"
echo(JSON.encode(obj))
return false
end
local data = JSON.decodeString(REQUEST.json)
if not data then
obj.error = "Invalid request"
echo(JSON.encode(obj))
return false
end
if data.status == 2 then
local tmpfile = "/tmp/"..std.sha1(ospath)
local cmd = "curl -o "..tmpfile..' "'..data.url..'"'
os.execute(cmd)
-- move file to correct position
if ulib.exists(tmpfile) then
cmd = "mv "..tmpfile.." "..ospath
os.execute(cmd)
print("File "..ospath.." sync with remote")
else
obj.error = "Unable to sync file"
echo(JSON.encode(obj))
return false
end
end
obj.result = "OK"
echo(JSON.encode(obj))
return false
end
function OfficeController:actionnotfound(...)
self.template:setView("index")
return self:index(table.unpack({...}))
end

View File

@ -13,7 +13,7 @@ local pre_process_md = function(str, obj)
"%%-")
if apath then
apath = apath:gsub(" ", "%%%%20")
--print(apath)
print(apath)
content = content:gsub(pattern, "![](" .. HTTP_ROOT .. "/" ..
obj.name .. "/asset/" .. apath .. ")")
end

View File

@ -22,8 +22,6 @@ POST_LIMIT = 10
-- require needed library
require(BASE_FRW.."silk.api")
package.path = package.path..";"..__ROOT__.."/os/libs/?.lua"
POLICY.mimes["model/gltf-binary"] = true
if REQUEST.r then

View File

@ -76,12 +76,12 @@
<?lua
if prev_entry then
echo("<a class = 'go_prev' href="..HTTP_ROOT..'/'..toc.controller..'/'..std.b64encode(prev_entry.path):gsub("=","")..'/'..prev_entry.name:gsub(" ", "_")..".md?show_toc=false".." >")
echo("<a class = 'go_prev' href="..HTTP_ROOT..'/'..toc.controller..'/'..std.b64encode(prev_entry.path):gsub("=","")..'/'..prev_entry.name:gsub(" ", "_")..".md".." >")
echo(prev_entry.name)
echo("</a>")
end
if next_entry then
echo("<a class = 'go_next' href="..HTTP_ROOT..'/'..toc.controller..'/'..std.b64encode(next_entry.path):gsub("=","")..'/'..next_entry.name:gsub(" ", "_")..".md?show_toc=false".." >")
echo("<a class = 'go_next' href="..HTTP_ROOT..'/'..toc.controller..'/'..std.b64encode(next_entry.path):gsub("=","")..'/'..next_entry.name:gsub(" ", "_")..".md".." >")
echo(next_entry.name)
echo("</a>")
end
@ -108,12 +108,12 @@ The comment editor supports <b>Markdown</b> syntax. Your email is necessary to n
<div class = "pagenav">
<?lua
if prev_entry then
echo("<a class = 'go_prev' href="..HTTP_ROOT..'/'..toc.controller..'/'..std.b64encode(prev_entry.path):gsub("=","")..'/'..prev_entry.name:gsub(" ", "_")..".md?show_toc=false".." >")
echo("<a class = 'go_prev' href="..HTTP_ROOT..'/'..toc.controller..'/'..std.b64encode(prev_entry.path):gsub("=","")..'/'..prev_entry.name:gsub(" ", "_")..".md".." >")
echo(prev_entry.name)
echo("</a>")
end
if next_entry then
echo("<a class = 'go_next' href="..HTTP_ROOT..'/'..toc.controller..'/'..std.b64encode(next_entry.path):gsub("=","")..'/'..next_entry.name:gsub(" ", "_")..".md?show_toc=false".." >")
echo("<a class = 'go_next' href="..HTTP_ROOT..'/'..toc.controller..'/'..std.b64encode(next_entry.path):gsub("=","")..'/'..next_entry.name:gsub(" ", "_")..".md".." >")
echo(next_entry.name)
echo("</a>")
end

View File

@ -3,20 +3,10 @@ local tocdata = __main__:get("toc")
local elinks = __main__:get("elinks")
local has_3d = __main__:get("has_3d")
local url = __main__:get("url")
local show_toc_css = ''
if REQUEST.show_toc and REQUEST.show_toc == "false" then
show_toc_css = 'style="display:none;"'
end
local book_width_css='style="max-width:80%;"'
if HEADER.mobile then
book_width_css='style="max-width:95%;"'
end
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link
rel="stylesheet"
type="text/css"
@ -25,7 +15,7 @@ end
rel="stylesheet"
type="text/css"
href="<?=HTTP_ROOT?>/rst/katex/katex.min.css" />
<script src="<?=HTTP_ROOT?>/rst/gscripts/jquery-3.4.1.min.js"> </script>
<script src="<?=HTTP_ROOT?>/rst/gscripts/jquery-3.2.1.min.js"> </script>
<?lua
if has_3d then
?>
@ -87,20 +77,28 @@ end
</head>
<body>
<div id = "top">
<div id = "navbar" <?=book_width_css?>>
<?lua
if tocdata then
?>
<div class = "doc-toc-menu">
<div id = "navbar">
<div class = "doc-name">
<?lua if tocdata then ?>
<a href ="#" id="btn_toc">
<a href ="<?=HTTP_ROOT..'/'..tocdata.controller..'/'?>">
<?=tocdata.data.name?>
</a>
<?lua end ?>
</div>
<?lua
if elinks then
for k,v in ipairs(elinks) do
?>
<a class = "x-link" target="_blank" href ="<?=v.url?>">
<?=v.name?>
</a>
<?lua
end
end
if tocdata then
?>
<form id = "search_form" action="<?=HTTP_ROOT..'/'..tocdata.controller..'/search/'?>" method="get" class="search-form">
<input id = "search_box" name="q" type = "text" class = "search-box"></input>
<input name="show_toc" type = "hidden" value="false"></input>
</form>
<div class= "search-icon"></div>
<?lua
@ -109,28 +107,11 @@ end
</div>
</div>
<div id = "cover">
<div id = "book" <?=book_width_css?>>
<?lua if tocdata then ?>
<div id="doc_toc" class = "doc-toc" <?=show_toc_css?> >
<div class = "doc-name doc-toc-header">
<a href ="<?=HTTP_ROOT..'/'..tocdata.controller..'/'?>">
<?=tocdata.data.name?>
</a>
</div>
<?lua
if elinks then
for k,v in ipairs(elinks) do
?>
<div class = "doc-toc-header">
<a class = "x-link" target="_blank" href ="<?=v.url?>">
<?=v.name?>
</a>
</div>
<?lua
end
end
?>
<div class = "doc-toc-header doc-toc-legend">Table of content</div>
<div id = "book">
<?lua
if tocdata then
?>
<div class = "doc-toc">
<?lua
if toc then
toc:set("data", tocdata)
@ -138,7 +119,7 @@ end
end
?>
</div>
<div class="doc-content markdown-body" id="doc_content">
<div class="doc-content markdown-body">
<?lua
if __main__ then
__main__:render()
@ -163,25 +144,7 @@ end
Powered by antd server, (c) 2019 - <?=os.date("*t").year?> Xuan Sang LE
</div>
<script>
const toc_class_toggle = () => {
if(!$("#doc_toc").is(":hidden"))
{
$("#btn_toc").attr("class", "toc-active");
}
else
{
$("#btn_toc").removeClass("toc-active");
}
};
window.addEventListener('load', (event) => {
$("#btn_toc").click(function(){
$("#doc_toc").toggle();
toc_class_toggle();
});
$("#doc_content").click(function(){
$("#doc_toc").hide();
toc_class_toggle();
});
// tree view events
var toggler = document.getElementsByClassName("caret");
var i;
@ -228,7 +191,7 @@ end
end
?>
});
toc_class_toggle();
</script>
</body>
</html>

View File

@ -1,67 +0,0 @@
<?lua
local shareid = __main__:get("shareid")
local ext = __main__:get("ext")
local doctype = __main__:get("doctype")
local name = __main__:get("name")
local key = __main__:get("key")
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link
rel="stylesheet"
type="text/css"
href="<?=HTTP_ROOT?>/assets/office.css" />
<!--meta name="viewport" content="width=device-width, initial-scale=1"-->
<script src="https://office.iohub.dev/web-apps/apps/api/documents/api.js"> </script>
<title>
<?lua
echo(name)
?>
</title>
<script type="text/javascript">
<?lua
if shareid and ext and doctype ~= "none" then
?>
window.onload = () => {
const editor = new DocsAPI.DocEditor("container", {
events: {
onAppReady: (e) => () => {},
//onRequestCreateNew: () => @newDocument(),
//onRequestSaveAs: (e) => @saveAs(e)
},
document: {
fileType: '<?=ext?>',
key: '<?=key?>',
title: '<?=name?>',
url: "https://doc.iohub.dev/office/shared/<?=shareid?>"
},
documentType: '<?=doctype?>',
editorConfig: {
//user: {
// id: @systemsetting.user.id.toString(),
// name: @systemsetting.user.username
//},
customization: {
compactHeader: false,
},
callbackUrl:"https://doc.iohub.dev/office/save/<?=shareid?>"
}
});
};
<?lua
end
?>
</script>
</head>
<body>
<div id = "container">
<?lua
if not shareid or not ext or doctype == "none" then
echo("<h1> Invalid document </h1>")
end
?>
</div>
</body>
</html>

View File

@ -33,7 +33,7 @@ function NotfoundController:index(...)
if ulib.exists(path) then
std.header("text/plain")
std.sendFile(path)
std.f(path)
else
self:error("No script found: "..path)
end

View File

@ -2,32 +2,23 @@
set -e
V_ANTD="1.0.6b"
V_LUA="0.5.2b"
#V_WTERM="1.0.0b"
V_TUNNEL="0.1.3b"
#V_CGI="1.0.0b"
V_PUBS="0.1.2a"
V_ANTOS=$(wget -qO- https://github.com/lxsang/antos/raw/1.2.1/release/latest)
mkdir -p /opt/www/htdocs
if [ "$1" = "full" ]; then
# base server
wget -O- https://get.iohub.dev/antd | bash -s "$V_ANTD"
wget -O- https://get.bitdojo.dev/antd | bash -s "1.0.6b"
# base plugin
wget -O- https://get.iohub.dev/antd_plugin | bash -s "lua-$V_LUA"
#wget -O- https://get.iohub.dev/antd_plugin | bash -s "wterm-$V_WTERM"
wget -O- https://get.iohub.dev/antd_plugin | bash -s "tunnel-$V_TUNNEL"
#wget -O- https://get.iohub.dev/antd_plugin | bash -s "cgi-$V_CGI"
wget -O- https://get.bitdojo.dev/antd_plugin | bash -s "lua-0.5.2b"
wget -O- https://get.bitdojo.dev/antd_plugin | bash -s "wterm-1.0.0b"
wget -O- https://get.bitdojo.dev/antd_plugin | bash -s "tunnel-0.1.0b"
# install antos
[ -d /tmp/apub ] && rm -r /tmp/apub
mkdir -p /tmp/apub
cd /tmp/apub
wget --no-check-certificate "https://github.com/lxsang/antd-tunnel-publishers/raw/master/dist/antd-publishers-$V_PUBS.tar.gz"
tar xvzf antd-publishers-$V_PUBS.tar.gz
cd antd-publishers-$V_PUBS
wget --no-check-certificate "https://github.com/lxsang/antd-tunnel-publishers/raw/master/dist/antd-publishers-0.1.0a.tar.gz"
tar xvzf antd-publishers-0.1.0a.tar.gz
cd antd-publishers-0.1.0a
./configure --prefix=/opt/www && make && make install
cd /opt/www
@ -104,11 +95,11 @@ cd /opt/www/htdocs
wget --no-check-certificate "https://github.com/lxsang/antd-web-apps/raw/master/dist/antd_web_apps.tar.gz"
tar xvzf antd_web_apps.tar.gz
rm antd_web_apps.tar.gz
rm -r blog doc index.ls info talk get
rm -r ci blog doc index.ls info talk get
cd /opt/www/htdocs/os
wget --no-check-certificate "https://github.com/lxsang/antos/raw/1.2.1/release/antos-$V_ANTOS.tar.gz"
tar xvzf antos-$V_ANTOS.tar.gz
rm antos-$V_ANTOS.tar.gz
wget --no-check-certificate "https://github.com/lxsang/antos/raw/master/release/antos-1.1.2.tar.gz"
tar xvzf antos-1.1.2.tar.gz
rm antos-1.1.2.tar.gz
echo "Install done..."
echo "Install done..."

View File

@ -19,7 +19,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hi, I'm <?=data.fullname?></title>
<title>Hi, I'm Xuan Sang LE</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="grs/ubuntu-regular.css" />
@ -39,7 +39,7 @@
<div id = "container" class="<?=mobilecls?>" >
<img src = "grs/images/mrsang.png" ></img>
<div id = "vcard">
<p class = "greeting">Hi, I'm <b><?=data.fullname?></b></p>
<p class = "greeting">Hi, I'm <b>Xuan Sang LE</b></p>
<p class = "dedicate">
<span class="fa fa-quote-left"></span>
<span>
@ -47,13 +47,12 @@
</span>
<span class="fa fa-quote-right"></span>
</p>
<a href="https://blog.iohub.dev" class ="about">Read my blog</a>
<a href="https://info.iohub.dev" class ="about">More about me</a>
<a href="https://info.lxsang.me" class ="about">Find out more about me</a>
</div>
</div>
</div>
<div id = "bottom">
Powered by antd server, (c) 2017 - 2022 Dany LE
Powered by antd server, (c) 2017 - 2018 Xuan Sang LE
</div>
</div>
</body>

View File

@ -53,8 +53,7 @@ end
function IndexController:pdf(...)
local tmp_file = WWW_ROOT.."/cv_exported.pdf"
local cmd = "wkhtmltopdf "..HTTP_ROOT.."/"..self.registry.user.."/index/notoc "..tmp_file
print(cmd)
local cmd = "wkhtmltopdf "..HTTP_ROOT.."/"..self.registry.user.."/notoc "..tmp_file
local r = os.execute(cmd)
if r then
local mime = std.mimeOf(tmp_file)

View File

@ -15,25 +15,3 @@ function UserController:index(...)
self.template:set("data", data[1])
return true
end
function UserController:photo(...)
local data = self.user:findAll()
if not data or not data[1] then
self:error("Cannot fetch user info")
end
if(not data[1] or data[1].photo == "") then
self:error("User photo is not available")
end
local prefix = data[1].photo:match("%a+://")
local suffix = data[1].photo:gsub(prefix,"")
local path = string.format("/home/%s/", self.registry.user)..suffix
print(path)
if ulib.exists(path) then
local mime = std.mimeOf(path)
std.sendFile(path)
else
self:error("Asset file not found or access forbidden: "..path)
end
return false
end

View File

@ -26,7 +26,7 @@ require(BASE_FRW.."silk.api")
local REGISTRY = {}
-- set logging level
REGISTRY.logger = Logger:new{ levels = {INFO = false, ERROR = true, DEBUG = false}}
REGISTRY.users_allowed = { phuong = true, mrsang = true, dany = true }
REGISTRY.users_allowed = { phuong = true, mrsang = true }
REGISTRY.user = "mrsang"
REGISTRY.db = DBHelper:new{db=REGISTRY.user}
REGISTRY.layout = 'default'
@ -67,7 +67,7 @@ function NotfoundController:index(...)
self:error("404: Controller "..args[1].." not found : "..args[2])
return
end
REQUEST.r = std.trim(REQUEST.r:gsub(user, ""), "/")
REQUEST.r = "index/"..std.trim(REQUEST.r:gsub(user, ""), "/")
if REGISTRY.db then REGISTRY.db:close() end
REGISTRY.user = user
REGISTRY.db = DBHelper:new{db=REGISTRY.user}

View File

@ -31,7 +31,7 @@
}
.layout div.container{
display: block;
display: none;
}
.layout div.container_active{
display: block;
@ -169,16 +169,4 @@ hr{
border-top: 1px solid #878887;
padding: 0;
}
img {max-width:100%}
.header_container {
display: block;
width: 100%;
}
.header_container img {
float: left;
max-width: 150px;
margin-right: 10px;
margin-bottom: 20px;
display: block;
}
img {max-width:100%}

View File

@ -8,8 +8,7 @@
local active = "toc_active"
for k, v in pairs(data) do
?>
<!--onclick='switchTab("toc<?=v[2]?>", this)'-->
<li class="<?=active?>"><a href=<?='"#toc'..v[2]..'"'?> ><?=v[1]?></a></li>
<li class="<?=active?>"><a href=<?='"#toc'..v[2]..'"'?> onclick='switchTab("toc<?=v[2]?>", this)' ><?=v[1]?></a></li>
<?lua
active = ''
end

View File

@ -1,36 +1,27 @@
<div class="header_container">
<?lua if data.photo and data.photo ~= "" and data.user ~= "mrsang" then ?>
<img src="/<?=data.user?>/user/photo"></img>
<?lua end ?>
<h1>
<span class="name"><?=data.fullname?></span>
<span class="cv">Curriculum Vitae</span>
</h1>
<p class="coordination">
<span class="fa fa-home"></span><?=data.address?></p>
<p class="coordination">
<span class="fa fa-phone"></span>
<span class="text"><?=data.Phone?></span>
<span class="fa fa-envelope-o"></span>
<span class="text"><?=data.email?></span>
<br/>
<span class="fa fa-globe"></span>
<span class="text"><a href ="<?=data.url?>"><?=data.url?></a></span>
<?lua
if not preview then
?>
<span class="fa fa-file-pdf-o"></span>
<span class="text"><a href ="<?=HTTP_ROOT?>/<?=data.user?>/index/pdf" target="_blank">Download</a></span>
<?lua
end
?>
</p>
<p class="shortbio">
<span class="fa fa-quote-left"></span>
<span><?=data.shortbiblio?></span>
<span class="fa fa-quote-right"></span>
</p>
</div>
<?lua if not HEADER.mobile and data.user == "mrsang" then ?>
<!--iframe width="770" height="330" src="https://mars.nasa.gov/layout/embed/send-your-name/future/certificate/?cn=792789419260" frameborder="0"></iframe-->
<?lua end ?>
<h1>
<span class="name"><?=data.fullname?></span>
<span class="cv">Curriculum Vitae</span>
</h1>
<p class="coordination">
<span class="fa fa-home"></span><?=data.address?></p>
<p class="coordination">
<span class="fa fa-phone"></span>
<span class="text"><?=data.Phone?></span>
<span class="fa fa-envelope-o"></span>
<span class="text"><?=data.email?></span>
<span class="fa fa-globe"></span>
<span class="text"><a href ="<?=data.url?>"><?=data.url?></a></span>
<?lua
if not preview then
?>
<span class="fa fa-file-pdf-o"></span>
<span class="text"><a href ="<?=HTTP_ROOT?>/<?=data.user?>/pdf" target="_blank">Download</a></span>
<?lua
end
?>
</p>
<p class="shortbio">
<span class="fa fa-quote-left"></span>
<span><?=data.shortbiblio?></span>
<span class="fa fa-quote-right"></span>
</p>

View File

@ -1,48 +1,5 @@
{
"odt": {
"mime": "application/vnd.oasis.opendocument.text",
"binary": true
},
"docx": {
"mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"binary": true
},
"doc": {
"mime": "application/msword",
"binary": true
},
"xls": {
"mime": "application/vnd.ms-excel",
"binary": true
},
"xlsx": {
"mime": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"binary": true
},
"ppt": {
"mime": "application/vnd.ms-powerpoint",
"binary": true
},
"pptx": {
"mime": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
"binary": true
},
"epub": {
"mime": "application/epub+zip",
"binary": true
},
"csv": {
"mime": "text/csv",
"binary": false
},
"ods": {
"mime": "application/vnd.oasis.opendocument.spreadsheet",
"binary": true
},
"odp": {
"mime": "application/vnd.oasis.opendocument.presentation",
"binary": true
},
"odt": { "mime": "application/vnd.oasis.opendocument.text", "binary": true },
"viz": {
"mime": "text/vnd.graphviz",
"binary": false
@ -51,7 +8,7 @@
"mime": "text/x-python",
"binary": false
},
"coffee": {
"coffee":{
"mime": "text/vnd.coffeescript",
"binary": false
},
@ -66,13 +23,5 @@
"glb": {
"mime": "model/gltf-binary",
"binary": true
},
"ts":{
"mime": "text/x.typescript",
"binary": false
},
"map":{
"mime": "application/json",
"binary": false
}
}

View File

@ -98,7 +98,6 @@ function SystemController:application(...)
end
function SystemController:apigateway(...)
local args={...}
local use_ws = false
if REQUEST and REQUEST.ws == "1" then
-- override the global echo command
@ -132,12 +131,10 @@ function SystemController:apigateway(...)
r, e = loadfile(ospath)
if r then
local status, result = pcall(r, data.parameters)
if result then
if (status) then
echo(JSON.encode(result))
else
echo(result)
end
if (status) then
echo(JSON.encode(result))
else
echo(result)
end
else
echo(e)
@ -159,9 +156,7 @@ function SystemController:apigateway(...)
if use_ws then
if std.ws.enable() then
-- read header
local header = nil
-- wait until we receive request
while(not header) do header = std.ws.header() end
local header = std.ws.header()
if header then
if header.mask == 0 then
print("Data is not masked")
@ -188,26 +183,10 @@ function SystemController:apigateway(...)
print("Web socket is not available.")
end
else
if REQUEST.path then
exec_with_user_priv(REQUEST)
elseif REQUEST.json then
if REQUEST.json then
data = JSON.decodeString(REQUEST.json)
--std.json()
exec_with_user_priv(data)
elseif args and #args > 0 then
-- data is encoded in url safe base64
local encoded = args[1]:gsub('_', '/'):gsub('-', '+')
if #encoded % 4 == 2 then
encoded = encoded.."=="
elseif #encoded %4 == 3 then
encoded = encoded.."="
end
local decoded = std.b64decode(encoded)
data = JSON.decodeString(bytes.__tostring(decoded))
if data and data.path then
exec_with_user_priv(data)
else
fail("Unknown request")
end
else
fail("Unkown request")
end

View File

@ -191,7 +191,6 @@ function VFSController:upload(...)
if r then
result(r)
else
self:error(m)
fail(m)
end
else

View File

@ -1,13 +1,10 @@
require("sqlite")
local TUNNEL_KEYCHAIN = "/opt/www/tmp/channels/antunnel_keychain"
function fail(msg)
std.custom_header("Connection","close")
std.json()
std.t(JSON.encode({error=msg}))
end
function result(obj)
std.custom_header("Connection","close")
std.json()
std.t(JSON.encode({result=obj, error=false}))
end
@ -29,33 +26,17 @@ function sysdb()
end
function is_auth()
local sessionid = nil
if SESSION.sessionid and SESSION.sessionid ~= '0' then
sessionid = SESSION.sessionid
-- should be used only by API call
elseif REQUEST.sessionid and REQUEST.sessionid ~= '0' then
sessionid = REQUEST.sessionid
elseif REQUEST.access_token and REQUEST.access_token ~= '0' then
sessionid = REQUEST.access_token
end
if sessionid == nil then
return false
end
if SESSION.sessionid == nil or SESSION.sessionid == '0' then return false end
-- query session id from database
local db = sysdb()
if db == nil then return false end
local cond = {exp= {["="] = { sessionid = sessionid }}}
local cond = {exp= {["="] = { sessionid = SESSION.sessionid }}}
local data = db:find(cond)
--print(JSON.encode(data))
db:close()
if data == nil or data[1] == nil then return die("No user data found") end
-- next time check the stamp
SESSION.user = data[1].username
local f = io.open(TUNNEL_KEYCHAIN, "w")
if f then
f:write(sessionid..SESSION.user)
f:close()
end
return true
end
@ -63,4 +44,4 @@ function auth_or_die(msg)
if(is_auth() == false) then
die(msg)
end
end
end

View File

@ -113,7 +113,7 @@ vfs.write = function(path,data)
local uid = ulib.uid(SESSION.user)
--
if data ~= "" then
local header = string.match(data, "^data%:[%w%.-%+]+%/[%w%.-%+]+;base64,")
local header = string.match(data, "^data%:[%w%.-]+%/[%w%.-]+;base64,")
if header ~= nil then
local b64data = string.gsub(data, utils.escape_pattern(header),"")
local barr = std.b64decode(b64data)
@ -141,23 +141,13 @@ vfs.write = function(path,data)
end
vfs.upload = function(path)
if(not path) then
return false, "Unknown upload destination, abort!"
end
local r,m = vfs.checkperm(path,"write")
if(r) then
local uid = ulib.uid(SESSION.user)
local index = 0
while(REQUEST["upload-"..index..".tmp"] ~= nil) do
local file = m.."/"..REQUEST["upload-"..index..".file"]
ulib.move(REQUEST["upload-"..index..".tmp"], file)
ulib.chown(file, uid.id, uid.gid)
index = index + 1
end
if(index == 0) then
return false, "No file is uploaded"
end
return true, index
local file = m.."/"..REQUEST["upload.file"]
ulib.move(REQUEST["upload.tmp"], file)
ulib.chown(file, uid.id, uid.gid)
return true, nil
else
return r,m
end
@ -166,14 +156,14 @@ end
vfs.checkperm = function(path, right)
local osfile = vfs.ospath(path)
local perm = vfs.perm(osfile)
--print(osfile)
print(osfile)
if not ulib.exists(osfile) then
return false,"Resource does not exist"
end
-- check if user own the file
if perm ~= nil then
if perm[right] == true then
--print("Permission granted")
print("Permission granted")
return true,osfile
else
print("Permission denie")
@ -192,13 +182,13 @@ vfs.perm = function(file)
if uid ~= nil and st ~= nil and st.perm ~= nil then
--print(JSON.encode({uid, st}))
if(uid.id == st.uid) then -- the user owned the file
--print("file belong to user")
print("file belong to user")
return st.perm.owner
elseif uid.groups and uid.groups[st.gid] then
--print("User belong to this group")
print("User belong to this group")
return st.perm.group
else
--print("User belong to other")
print("User belong to other")
return st.perm.other
end
else