mirror of
https://github.com/lxsang/antd-web-apps
synced 2025-07-16 13:59:52 +02:00
Compare commits
38 Commits
fd31477739
...
ci
Author | SHA1 | Date | |
---|---|---|---|
bed7fc6ec0 | |||
a361a475c9 | |||
dd0f7bc860 | |||
71a661bc4a | |||
291a45cdb5 | |||
4ba7bd6b24 | |||
47eab0a1dc | |||
be1f9e9fe1 | |||
c076bd68b2 | |||
185f676dd6 | |||
7f326baad5 | |||
91f15fd884 | |||
cd30af6da4 | |||
05ee884e8d | |||
d96a9c699e | |||
fda4a23943 | |||
acbbe8f0a2 | |||
99414311e5 | |||
3f7bc5fa7d | |||
dd3e56fb01 | |||
5c5d3504ad | |||
53e2b3b891 | |||
6bde15ff78 | |||
90b812f35a | |||
81f8e1c984 | |||
6e6b7930bc | |||
7d05a51cff | |||
f03d18499b | |||
8de304f2c6 | |||
f684005fe4 | |||
257f1e42bd | |||
db730f7220 | |||
f04a756b4c | |||
396f1950dc | |||
2902e95e86 | |||
3af5abdf87 | |||
7e96479136 | |||
36c73c8035 |
22
.drone.yml
22
.drone.yml
@ -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
49
Jenkinsfile
vendored
@ -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
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
8
Makefile
8
Makefile
@ -1,12 +1,12 @@
|
|||||||
BUILDDIR?=./build
|
BUILDDIR?=./build
|
||||||
PROJS?=grs info blog os doc talk get
|
PROJS?=grs info blog os doc ci talk get
|
||||||
copyfiles = index.ls mimes.json
|
copyfiles = index.ls mimes.json
|
||||||
main: copy
|
main: copy
|
||||||
for f in $(PROJS); do BUILDDIR=$(BUILDDIR)/"$${f}" make -C "$${f}" ; done
|
for f in $(PROJS); do BUILDDIR=$(BUILDDIR)/"$${f}" make -C "$${f}" ; done
|
||||||
|
|
||||||
copy:
|
copy:
|
||||||
cp -rfv $(copyfiles) $(BUILDDIR)
|
cp -rf $(copyfiles) $(BUILDDIR)
|
||||||
cp -rv silk $(BUILDDIR)
|
cp -r silk $(BUILDDIR)
|
||||||
|
|
||||||
ar:
|
ar:
|
||||||
-[ -d /tmp/antd_web_apps ] && rm -r /tmp/antd_web_apps
|
-[ -d /tmp/antd_web_apps ] && rm -r /tmp/antd_web_apps
|
||||||
@ -19,4 +19,4 @@ ar:
|
|||||||
clean:
|
clean:
|
||||||
-for f in $(PROJS); do rm -r $(BUILDDIR)/"$${f}"; done
|
-for f in $(PROJS); do rm -r $(BUILDDIR)/"$${f}"; done
|
||||||
-for f in $(copyfiles); do rm -r $(BUILDDIR)/"$${f}"; done
|
-for f in $(copyfiles); do rm -r $(BUILDDIR)/"$${f}"; done
|
||||||
-rm -r $(BUILDDIR)/silk
|
-rm -r $(BUILDDIR)/silk
|
@ -1,2 +1,2 @@
|
|||||||
# antd-web-apps
|
# antd-web-apps
|
||||||
Various web apps for antd server
|
Some web apps for antd server
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
copyfiles = assets views models ai controllers router.lua
|
copyfiles = assets views models controllers router.lua
|
||||||
|
|
||||||
main:
|
main:
|
||||||
- mkdir -p $(BUILDDIR)
|
- mkdir -p $(BUILDDIR)
|
||||||
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
@ -18,6 +18,7 @@ function subscribe(prefix) {
|
|||||||
scheme = undefined;
|
scheme = undefined;
|
||||||
});
|
});
|
||||||
obs.on("rendered", function (d) {
|
obs.on("rendered", function (d) {
|
||||||
|
console.log("rednered");
|
||||||
$(".afx-window-title", scheme).html("Subscribe");
|
$(".afx-window-title", scheme).html("Subscribe");
|
||||||
$("[data-id='send']", scheme).click(function () {
|
$("[data-id='send']", scheme).click(function () {
|
||||||
var status = $("[data-id='status']", scheme);
|
var status = $("[data-id='status']", scheme);
|
||||||
|
@ -73,52 +73,6 @@ body {
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
flex-direction: row;
|
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 {
|
#navbar.navmobile {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
|
@ -88,43 +88,6 @@ function PostController:bytag(b64tag, limit, action, id)
|
|||||||
return true
|
return true
|
||||||
end
|
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)
|
function PostController:id(pid)
|
||||||
local data, order = self.blog:fetch({["="] = {id = pid}})
|
local data, order = self.blog:fetch({["="] = {id = pid}})
|
||||||
if not data or #order == 0 then
|
if not data or #order == 0 then
|
||||||
@ -167,57 +130,11 @@ function PostController:actionnotfound(...)
|
|||||||
return self:notfound("Action [" .. args[1] .. "] not found")
|
return self:notfound("Action [" .. args[1] .. "] not found")
|
||||||
end
|
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)
|
function PostController:analyse(n)
|
||||||
if not n then
|
if not n then
|
||||||
n = 5
|
n = 5
|
||||||
end
|
end
|
||||||
local path = WWW_ROOT..DIR_SEP.."ai"
|
local path = "/home/mrsang/aiws/blog-clustering"
|
||||||
local gettext = loadfile(path .. "/gettext.lua")()
|
local gettext = loadfile(path .. "/gettext.lua")()
|
||||||
local cluster = loadfile(path .. "/cluster.lua")()
|
local cluster = loadfile(path .. "/cluster.lua")()
|
||||||
local data = gettext.get({publish = 1})
|
local data = gettext.get({publish = 1})
|
||||||
@ -236,7 +153,7 @@ function PostController:analyse(n)
|
|||||||
self.analytical:delete({["="] = {["1"] = 1}})
|
self.analytical:delete({["="] = {["1"] = 1}})
|
||||||
-- get similarity and put to the table
|
-- get similarity and put to the table
|
||||||
for id, v in pairs(vectors) do
|
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
|
for a, b in pairs(top) do
|
||||||
local record = {pid = id, sid = a, score = b}
|
local record = {pid = id, sid = a, score = b}
|
||||||
self.analytical:create(record)
|
self.analytical:create(record)
|
||||||
|
@ -27,7 +27,7 @@ function BlogModel:fetch(cnd, limit, order)
|
|||||||
exp = {["and"] = exp },
|
exp = {["and"] = exp },
|
||||||
order = { ctime = "DESC" },
|
order = { ctime = "DESC" },
|
||||||
fields = {
|
fields = {
|
||||||
"id", "title", "utime", "ctime", "utimestr", "content", "ctimestr", "rendered", "tags"
|
"id", "title", "utime", "ctime", "utimestr", "ctimestr", "rendered", "tags"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if limit then
|
if limit then
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
local render = __main__:get("render")
|
local render = __main__:get("render")
|
||||||
local url = __main__:get("url")
|
local url = __main__:get("url")
|
||||||
local tags = __main__:get("tags")
|
local tags = __main__:get("tags")
|
||||||
local d3 = __main__:get("d3")
|
|
||||||
local cls = ""
|
local cls = ""
|
||||||
if HEADER.mobile then
|
if HEADER.mobile then
|
||||||
cls = "navmobile"
|
cls = "navmobile"
|
||||||
@ -28,16 +27,8 @@
|
|||||||
<script src="https://chat.iohub.dev/assets/quicktalk.js"> </script>
|
<script src="https://chat.iohub.dev/assets/quicktalk.js"> </script>
|
||||||
|
|
||||||
<script src="<?=HTTP_ROOT?>/rst/afx.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>
|
<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="" />
|
<meta property="og:image" content="" />
|
||||||
<?lua if render then ?>
|
<?lua if render then ?>
|
||||||
<meta name="twitter:card" content="summary" />
|
<meta name="twitter:card" content="summary" />
|
||||||
@ -125,14 +116,14 @@
|
|||||||
<div class = "logo"><a href = "https://lxsang.me"></a></div>
|
<div class = "logo"><a href = "https://lxsang.me"></a></div>
|
||||||
<ul>
|
<ul>
|
||||||
<li><i class = "fa fa-home"></i><a href="<?=HTTP_ROOT?>">Home</a></li>
|
<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
|
<?lua
|
||||||
if not HEADER.mobile then
|
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-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 ?>
|
<?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>
|
</ul>
|
||||||
<?lua
|
<?lua
|
||||||
if not HEADER.mobile then
|
if not HEADER.mobile then
|
||||||
@ -152,7 +143,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id = "bottom">
|
<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>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -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> Close</a>
|
|
||||||
<a id="floating_btn_read_more" href="#"><i class="fa fa-chain"></i> 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>
|
|
@ -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
|
<?lua
|
||||||
local datas = posts
|
local datas = posts
|
||||||
local class = "card"
|
local class = "card"
|
||||||
|
6
ci/Makefile
Normal file
6
ci/Makefile
Normal 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
82
ci/router.lua
Normal 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()
|
||||||
|
|
31
ci/scripts/antd-web-apps.sh
Normal file
31
ci/scripts/antd-web-apps.sh
Normal 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
31
ci/scripts/antos.sh
Normal 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"
|
BIN
dist/antd_web_apps.tar.gz
vendored
BIN
dist/antd_web_apps.tar.gz
vendored
Binary file not shown.
@ -1,4 +0,0 @@
|
|||||||
html, body {
|
|
||||||
margin: 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
@ -29,7 +29,6 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-top: 1px solid #878887;
|
border-top: 1px solid #878887;
|
||||||
z-index: 22;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#cover {
|
#cover {
|
||||||
@ -39,14 +38,14 @@ body {
|
|||||||
}
|
}
|
||||||
#navbar {
|
#navbar {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
/* max-width: 80%; */
|
max-width: 80%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
#book {
|
#book {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
/* max-width: 80%; */
|
max-width: 80%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
@ -54,36 +53,10 @@ body {
|
|||||||
text-align: justify;
|
text-align: justify;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
div.doc-toc-menu {
|
div.doc-name {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding-top: 3px;
|
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 {
|
div.doc-name a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #c9c9c9;
|
color: #c9c9c9;
|
||||||
@ -92,14 +65,13 @@ div.doc-name a {
|
|||||||
a.x-link,
|
a.x-link,
|
||||||
a.x-link:hover {
|
a.x-link:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #2c2c2c;
|
color: #c9c9c9;
|
||||||
font-weight: bold;
|
padding-left: 15px;
|
||||||
/* padding-left: 15px; */
|
|
||||||
padding-top: 7px;
|
padding-top: 7px;
|
||||||
}
|
}
|
||||||
a.x-link::before {
|
a.x-link::before {
|
||||||
content: "\f08e";
|
content: "\f08e";
|
||||||
color: #2c2c2c;
|
color: #c9c9c9;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
font-family: "FontAwesome";
|
font-family: "FontAwesome";
|
||||||
@ -108,7 +80,7 @@ a.x-link::before {
|
|||||||
div.doc-name a::before {
|
div.doc-name a::before {
|
||||||
/* padding-top:13px; */
|
/* padding-top:13px; */
|
||||||
content: "\f015";
|
content: "\f015";
|
||||||
color: #2c2c2c;
|
color: #c9c9c9;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
font-family: "FontAwesome";
|
font-family: "FontAwesome";
|
||||||
@ -145,8 +117,8 @@ div.search-icon {
|
|||||||
width: 35px;
|
width: 35px;
|
||||||
}
|
}
|
||||||
div.doc-toc {
|
div.doc-toc {
|
||||||
max-width: 70%;
|
width: 300px;
|
||||||
padding-right: 0;
|
/* font-size: 11px; */
|
||||||
background-color: #e3e3e3;
|
background-color: #e3e3e3;
|
||||||
color: #2c2c2c;
|
color: #2c2c2c;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@ -154,38 +126,8 @@ div.doc-toc {
|
|||||||
top: 30px;
|
top: 30px;
|
||||||
bottom: 30px;
|
bottom: 30px;
|
||||||
border-right: 1px solid #c9c9c9;
|
border-right: 1px solid #c9c9c9;
|
||||||
border-left: 1px solid #c9c9c9;
|
padding-top: 10px;
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
box-shadow: 0px 6px 3px -1px rgba(0,0,0,0.65);
|
|
||||||
z-index: 20;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
div.doc-toc a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #2c2c2c;
|
color: #2c2c2c;
|
||||||
@ -195,7 +137,6 @@ div.doc-toc a {
|
|||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
|
||||||
}
|
}
|
||||||
div.doc-toc ul.nested {
|
div.doc-toc ul.nested {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
@ -237,7 +178,7 @@ div.doc-toc a.highlight {
|
|||||||
|
|
||||||
div.doc-content {
|
div.doc-content {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: calc(100% - 300px);
|
||||||
float: right;
|
float: right;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
@ -13,7 +13,7 @@ local pre_process_md = function(str, obj)
|
|||||||
"%%-")
|
"%%-")
|
||||||
if apath then
|
if apath then
|
||||||
apath = apath:gsub(" ", "%%%%20")
|
apath = apath:gsub(" ", "%%%%20")
|
||||||
--print(apath)
|
print(apath)
|
||||||
content = content:gsub(pattern, "")
|
obj.name .. "/asset/" .. apath .. ")")
|
||||||
end
|
end
|
||||||
|
@ -22,8 +22,6 @@ POST_LIMIT = 10
|
|||||||
-- require needed library
|
-- require needed library
|
||||||
require(BASE_FRW.."silk.api")
|
require(BASE_FRW.."silk.api")
|
||||||
|
|
||||||
package.path = package.path..";"..__ROOT__.."/os/libs/?.lua"
|
|
||||||
|
|
||||||
POLICY.mimes["model/gltf-binary"] = true
|
POLICY.mimes["model/gltf-binary"] = true
|
||||||
|
|
||||||
if REQUEST.r then
|
if REQUEST.r then
|
||||||
|
@ -76,12 +76,12 @@
|
|||||||
<?lua
|
<?lua
|
||||||
|
|
||||||
if prev_entry then
|
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(prev_entry.name)
|
||||||
echo("</a>")
|
echo("</a>")
|
||||||
end
|
end
|
||||||
if next_entry then
|
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(next_entry.name)
|
||||||
echo("</a>")
|
echo("</a>")
|
||||||
end
|
end
|
||||||
@ -108,12 +108,12 @@ The comment editor supports <b>Markdown</b> syntax. Your email is necessary to n
|
|||||||
<div class = "pagenav">
|
<div class = "pagenav">
|
||||||
<?lua
|
<?lua
|
||||||
if prev_entry then
|
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(prev_entry.name)
|
||||||
echo("</a>")
|
echo("</a>")
|
||||||
end
|
end
|
||||||
if next_entry then
|
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(next_entry.name)
|
||||||
echo("</a>")
|
echo("</a>")
|
||||||
end
|
end
|
||||||
|
@ -3,20 +3,10 @@ local tocdata = __main__:get("toc")
|
|||||||
local elinks = __main__:get("elinks")
|
local elinks = __main__:get("elinks")
|
||||||
local has_3d = __main__:get("has_3d")
|
local has_3d = __main__:get("has_3d")
|
||||||
local url = __main__:get("url")
|
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>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
type="text/css"
|
type="text/css"
|
||||||
@ -25,7 +15,7 @@ end
|
|||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
type="text/css"
|
type="text/css"
|
||||||
href="<?=HTTP_ROOT?>/rst/katex/katex.min.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
|
<?lua
|
||||||
if has_3d then
|
if has_3d then
|
||||||
?>
|
?>
|
||||||
@ -87,20 +77,28 @@ end
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id = "top">
|
<div id = "top">
|
||||||
<div id = "navbar" <?=book_width_css?>>
|
<div id = "navbar">
|
||||||
<?lua
|
<div class = "doc-name">
|
||||||
if tocdata then
|
|
||||||
?>
|
|
||||||
<div class = "doc-toc-menu">
|
|
||||||
<?lua if tocdata then ?>
|
<?lua if tocdata then ?>
|
||||||
<a href ="#" id="btn_toc">
|
<a href ="<?=HTTP_ROOT..'/'..tocdata.controller..'/'?>">
|
||||||
<?=tocdata.data.name?>
|
<?=tocdata.data.name?>
|
||||||
</a>
|
</a>
|
||||||
<?lua end ?>
|
<?lua end ?>
|
||||||
</div>
|
</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">
|
<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 id = "search_box" name="q" type = "text" class = "search-box"></input>
|
||||||
<input name="show_toc" type = "hidden" value="false"></input>
|
|
||||||
</form>
|
</form>
|
||||||
<div class= "search-icon"></div>
|
<div class= "search-icon"></div>
|
||||||
<?lua
|
<?lua
|
||||||
@ -109,28 +107,11 @@ end
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id = "cover">
|
<div id = "cover">
|
||||||
<div id = "book" <?=book_width_css?>>
|
<div id = "book">
|
||||||
<?lua if tocdata then ?>
|
<?lua
|
||||||
<div id="doc_toc" class = "doc-toc" <?=show_toc_css?> >
|
if tocdata then
|
||||||
<div class = "doc-name doc-toc-header">
|
?>
|
||||||
<a href ="<?=HTTP_ROOT..'/'..tocdata.controller..'/'?>">
|
<div class = "doc-toc">
|
||||||
<?=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>
|
|
||||||
<?lua
|
<?lua
|
||||||
if toc then
|
if toc then
|
||||||
toc:set("data", tocdata)
|
toc:set("data", tocdata)
|
||||||
@ -138,7 +119,7 @@ end
|
|||||||
end
|
end
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<div class="doc-content markdown-body" id="doc_content">
|
<div class="doc-content markdown-body">
|
||||||
<?lua
|
<?lua
|
||||||
if __main__ then
|
if __main__ then
|
||||||
__main__:render()
|
__main__:render()
|
||||||
@ -163,25 +144,7 @@ end
|
|||||||
Powered by antd server, (c) 2019 - <?=os.date("*t").year?> Xuan Sang LE
|
Powered by antd server, (c) 2019 - <?=os.date("*t").year?> Xuan Sang LE
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<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) => {
|
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
|
// tree view events
|
||||||
var toggler = document.getElementsByClassName("caret");
|
var toggler = document.getElementsByClassName("caret");
|
||||||
var i;
|
var i;
|
||||||
@ -228,7 +191,7 @@ end
|
|||||||
end
|
end
|
||||||
?>
|
?>
|
||||||
});
|
});
|
||||||
toc_class_toggle();
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -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>
|
|
@ -33,7 +33,7 @@ function NotfoundController:index(...)
|
|||||||
|
|
||||||
if ulib.exists(path) then
|
if ulib.exists(path) then
|
||||||
std.header("text/plain")
|
std.header("text/plain")
|
||||||
std.sendFile(path)
|
std.f(path)
|
||||||
else
|
else
|
||||||
self:error("No script found: "..path)
|
self:error("No script found: "..path)
|
||||||
end
|
end
|
||||||
|
@ -2,32 +2,23 @@
|
|||||||
|
|
||||||
set -e
|
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
|
mkdir -p /opt/www/htdocs
|
||||||
|
|
||||||
if [ "$1" = "full" ]; then
|
if [ "$1" = "full" ]; then
|
||||||
# base server
|
# 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
|
# base plugin
|
||||||
wget -O- https://get.iohub.dev/antd_plugin | bash -s "lua-$V_LUA"
|
wget -O- https://get.bitdojo.dev/antd_plugin | bash -s "lua-0.5.2b"
|
||||||
#wget -O- https://get.iohub.dev/antd_plugin | bash -s "wterm-$V_WTERM"
|
wget -O- https://get.bitdojo.dev/antd_plugin | bash -s "wterm-1.0.0b"
|
||||||
wget -O- https://get.iohub.dev/antd_plugin | bash -s "tunnel-$V_TUNNEL"
|
wget -O- https://get.bitdojo.dev/antd_plugin | bash -s "tunnel-0.1.0b"
|
||||||
#wget -O- https://get.iohub.dev/antd_plugin | bash -s "cgi-$V_CGI"
|
|
||||||
# install antos
|
# install antos
|
||||||
|
|
||||||
[ -d /tmp/apub ] && rm -r /tmp/apub
|
[ -d /tmp/apub ] && rm -r /tmp/apub
|
||||||
mkdir -p /tmp/apub
|
mkdir -p /tmp/apub
|
||||||
cd /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"
|
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-$V_PUBS.tar.gz
|
tar xvzf antd-publishers-0.1.0a.tar.gz
|
||||||
cd antd-publishers-$V_PUBS
|
cd antd-publishers-0.1.0a
|
||||||
./configure --prefix=/opt/www && make && make install
|
./configure --prefix=/opt/www && make && make install
|
||||||
|
|
||||||
cd /opt/www
|
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"
|
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
|
tar xvzf antd_web_apps.tar.gz
|
||||||
rm 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
|
cd /opt/www/htdocs/os
|
||||||
wget --no-check-certificate "https://github.com/lxsang/antos/raw/1.2.1/release/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-$V_ANTOS.tar.gz
|
tar xvzf antos-1.1.2.tar.gz
|
||||||
rm antos-$V_ANTOS.tar.gz
|
rm antos-1.1.2.tar.gz
|
||||||
|
|
||||||
echo "Install done..."
|
echo "Install done..."
|
9
index.ls
9
index.ls
@ -19,7 +19,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Hi, I'm <?=data.fullname?></title>
|
<title>Hi, I'm Xuan Sang LE</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" type="text/css" href="grs/ubuntu-regular.css" />
|
<link rel="stylesheet" type="text/css" href="grs/ubuntu-regular.css" />
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<div id = "container" class="<?=mobilecls?>" >
|
<div id = "container" class="<?=mobilecls?>" >
|
||||||
<img src = "grs/images/mrsang.png" ></img>
|
<img src = "grs/images/mrsang.png" ></img>
|
||||||
<div id = "vcard">
|
<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">
|
<p class = "dedicate">
|
||||||
<span class="fa fa-quote-left"></span>
|
<span class="fa fa-quote-left"></span>
|
||||||
<span>
|
<span>
|
||||||
@ -47,13 +47,12 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="fa fa-quote-right"></span>
|
<span class="fa fa-quote-right"></span>
|
||||||
</p>
|
</p>
|
||||||
<a href="https://blog.iohub.dev" class ="about">Read my blog</a>
|
<a href="https://info.lxsang.me" class ="about">Find out more about me</a>
|
||||||
<a href="https://info.iohub.dev" class ="about">More about me</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id = "bottom">
|
<div id = "bottom">
|
||||||
Powered by antd server, (c) 2017 - 2022 Dany LE
|
Powered by antd server, (c) 2017 - 2018 Xuan Sang LE
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
@ -53,8 +53,7 @@ end
|
|||||||
|
|
||||||
function IndexController:pdf(...)
|
function IndexController:pdf(...)
|
||||||
local tmp_file = WWW_ROOT.."/cv_exported.pdf"
|
local tmp_file = WWW_ROOT.."/cv_exported.pdf"
|
||||||
local cmd = "wkhtmltopdf "..HTTP_ROOT.."/"..self.registry.user.."/index/notoc "..tmp_file
|
local cmd = "wkhtmltopdf "..HTTP_ROOT.."/"..self.registry.user.."/notoc "..tmp_file
|
||||||
print(cmd)
|
|
||||||
local r = os.execute(cmd)
|
local r = os.execute(cmd)
|
||||||
if r then
|
if r then
|
||||||
local mime = std.mimeOf(tmp_file)
|
local mime = std.mimeOf(tmp_file)
|
||||||
|
@ -15,25 +15,3 @@ function UserController:index(...)
|
|||||||
self.template:set("data", data[1])
|
self.template:set("data", data[1])
|
||||||
return true
|
return true
|
||||||
end
|
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
|
|
@ -26,7 +26,7 @@ require(BASE_FRW.."silk.api")
|
|||||||
local REGISTRY = {}
|
local REGISTRY = {}
|
||||||
-- set logging level
|
-- set logging level
|
||||||
REGISTRY.logger = Logger:new{ levels = {INFO = false, ERROR = true, DEBUG = false}}
|
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.user = "mrsang"
|
||||||
REGISTRY.db = DBHelper:new{db=REGISTRY.user}
|
REGISTRY.db = DBHelper:new{db=REGISTRY.user}
|
||||||
REGISTRY.layout = 'default'
|
REGISTRY.layout = 'default'
|
||||||
@ -67,7 +67,7 @@ function NotfoundController:index(...)
|
|||||||
self:error("404: Controller "..args[1].." not found : "..args[2])
|
self:error("404: Controller "..args[1].." not found : "..args[2])
|
||||||
return
|
return
|
||||||
end
|
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
|
if REGISTRY.db then REGISTRY.db:close() end
|
||||||
REGISTRY.user = user
|
REGISTRY.user = user
|
||||||
REGISTRY.db = DBHelper:new{db=REGISTRY.user}
|
REGISTRY.db = DBHelper:new{db=REGISTRY.user}
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.layout div.container{
|
.layout div.container{
|
||||||
display: block;
|
display: none;
|
||||||
}
|
}
|
||||||
.layout div.container_active{
|
.layout div.container_active{
|
||||||
display: block;
|
display: block;
|
||||||
@ -169,16 +169,4 @@ hr{
|
|||||||
border-top: 1px solid #878887;
|
border-top: 1px solid #878887;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
img {max-width:100%}
|
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;
|
|
||||||
}
|
|
@ -8,8 +8,7 @@
|
|||||||
local active = "toc_active"
|
local active = "toc_active"
|
||||||
for k, v in pairs(data) do
|
for k, v in pairs(data) do
|
||||||
?>
|
?>
|
||||||
<!--onclick='switchTab("toc<?=v[2]?>", this)'-->
|
<li class="<?=active?>"><a href=<?='"#toc'..v[2]..'"'?> onclick='switchTab("toc<?=v[2]?>", this)' ><?=v[1]?></a></li>
|
||||||
<li class="<?=active?>"><a href=<?='"#toc'..v[2]..'"'?> ><?=v[1]?></a></li>
|
|
||||||
<?lua
|
<?lua
|
||||||
active = ''
|
active = ''
|
||||||
end
|
end
|
||||||
|
@ -1,36 +1,27 @@
|
|||||||
<div class="header_container">
|
<h1>
|
||||||
<?lua if data.photo and data.photo ~= "" and data.user ~= "mrsang" then ?>
|
<span class="name"><?=data.fullname?></span>
|
||||||
<img src="/<?=data.user?>/user/photo"></img>
|
<span class="cv">Curriculum Vitae</span>
|
||||||
<?lua end ?>
|
</h1>
|
||||||
<h1>
|
<p class="coordination">
|
||||||
<span class="name"><?=data.fullname?></span>
|
<span class="fa fa-home"></span><?=data.address?></p>
|
||||||
<span class="cv">Curriculum Vitae</span>
|
<p class="coordination">
|
||||||
</h1>
|
<span class="fa fa-phone"></span>
|
||||||
<p class="coordination">
|
<span class="text"><?=data.Phone?></span>
|
||||||
<span class="fa fa-home"></span><?=data.address?></p>
|
<span class="fa fa-envelope-o"></span>
|
||||||
<p class="coordination">
|
<span class="text"><?=data.email?></span>
|
||||||
<span class="fa fa-phone"></span>
|
<span class="fa fa-globe"></span>
|
||||||
<span class="text"><?=data.Phone?></span>
|
<span class="text"><a href ="<?=data.url?>"><?=data.url?></a></span>
|
||||||
<span class="fa fa-envelope-o"></span>
|
<?lua
|
||||||
<span class="text"><?=data.email?></span>
|
if not preview then
|
||||||
<br/>
|
?>
|
||||||
<span class="fa fa-globe"></span>
|
<span class="fa fa-file-pdf-o"></span>
|
||||||
<span class="text"><a href ="<?=data.url?>"><?=data.url?></a></span>
|
<span class="text"><a href ="<?=HTTP_ROOT?>/<?=data.user?>/pdf" target="_blank">Download</a></span>
|
||||||
<?lua
|
<?lua
|
||||||
if not preview then
|
end
|
||||||
?>
|
?>
|
||||||
<span class="fa fa-file-pdf-o"></span>
|
</p>
|
||||||
<span class="text"><a href ="<?=HTTP_ROOT?>/<?=data.user?>/index/pdf" target="_blank">Download</a></span>
|
<p class="shortbio">
|
||||||
<?lua
|
<span class="fa fa-quote-left"></span>
|
||||||
end
|
<span><?=data.shortbiblio?></span>
|
||||||
?>
|
<span class="fa fa-quote-right"></span>
|
||||||
</p>
|
</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 ?>
|
|
55
mimes.json
55
mimes.json
@ -1,48 +1,5 @@
|
|||||||
{
|
{
|
||||||
"odt": {
|
"odt": { "mime": "application/vnd.oasis.opendocument.text", "binary": true },
|
||||||
"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
|
|
||||||
},
|
|
||||||
"viz": {
|
"viz": {
|
||||||
"mime": "text/vnd.graphviz",
|
"mime": "text/vnd.graphviz",
|
||||||
"binary": false
|
"binary": false
|
||||||
@ -51,7 +8,7 @@
|
|||||||
"mime": "text/x-python",
|
"mime": "text/x-python",
|
||||||
"binary": false
|
"binary": false
|
||||||
},
|
},
|
||||||
"coffee": {
|
"coffee":{
|
||||||
"mime": "text/vnd.coffeescript",
|
"mime": "text/vnd.coffeescript",
|
||||||
"binary": false
|
"binary": false
|
||||||
},
|
},
|
||||||
@ -66,13 +23,5 @@
|
|||||||
"glb": {
|
"glb": {
|
||||||
"mime": "model/gltf-binary",
|
"mime": "model/gltf-binary",
|
||||||
"binary": true
|
"binary": true
|
||||||
},
|
|
||||||
"ts":{
|
|
||||||
"mime": "text/x.typescript",
|
|
||||||
"binary": false
|
|
||||||
},
|
|
||||||
"map":{
|
|
||||||
"mime": "application/json",
|
|
||||||
"binary": false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -98,7 +98,6 @@ function SystemController:application(...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function SystemController:apigateway(...)
|
function SystemController:apigateway(...)
|
||||||
local args={...}
|
|
||||||
local use_ws = false
|
local use_ws = false
|
||||||
if REQUEST and REQUEST.ws == "1" then
|
if REQUEST and REQUEST.ws == "1" then
|
||||||
-- override the global echo command
|
-- override the global echo command
|
||||||
@ -132,12 +131,10 @@ function SystemController:apigateway(...)
|
|||||||
r, e = loadfile(ospath)
|
r, e = loadfile(ospath)
|
||||||
if r then
|
if r then
|
||||||
local status, result = pcall(r, data.parameters)
|
local status, result = pcall(r, data.parameters)
|
||||||
if result then
|
if (status) then
|
||||||
if (status) then
|
echo(JSON.encode(result))
|
||||||
echo(JSON.encode(result))
|
else
|
||||||
else
|
echo(result)
|
||||||
echo(result)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
echo(e)
|
echo(e)
|
||||||
@ -159,9 +156,7 @@ function SystemController:apigateway(...)
|
|||||||
if use_ws then
|
if use_ws then
|
||||||
if std.ws.enable() then
|
if std.ws.enable() then
|
||||||
-- read header
|
-- read header
|
||||||
local header = nil
|
local header = std.ws.header()
|
||||||
-- wait until we receive request
|
|
||||||
while(not header) do header = std.ws.header() end
|
|
||||||
if header then
|
if header then
|
||||||
if header.mask == 0 then
|
if header.mask == 0 then
|
||||||
print("Data is not masked")
|
print("Data is not masked")
|
||||||
@ -188,26 +183,10 @@ function SystemController:apigateway(...)
|
|||||||
print("Web socket is not available.")
|
print("Web socket is not available.")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if REQUEST.path then
|
if REQUEST.json then
|
||||||
exec_with_user_priv(REQUEST)
|
|
||||||
elseif REQUEST.json then
|
|
||||||
data = JSON.decodeString(REQUEST.json)
|
data = JSON.decodeString(REQUEST.json)
|
||||||
|
--std.json()
|
||||||
exec_with_user_priv(data)
|
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
|
else
|
||||||
fail("Unkown request")
|
fail("Unkown request")
|
||||||
end
|
end
|
||||||
|
@ -191,7 +191,6 @@ function VFSController:upload(...)
|
|||||||
if r then
|
if r then
|
||||||
result(r)
|
result(r)
|
||||||
else
|
else
|
||||||
self:error(m)
|
|
||||||
fail(m)
|
fail(m)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
require("sqlite")
|
require("sqlite")
|
||||||
local TUNNEL_KEYCHAIN = "/opt/www/tmp/channels/antunnel_keychain"
|
|
||||||
function fail(msg)
|
function fail(msg)
|
||||||
std.custom_header("Connection","close")
|
|
||||||
std.json()
|
std.json()
|
||||||
std.t(JSON.encode({error=msg}))
|
std.t(JSON.encode({error=msg}))
|
||||||
end
|
end
|
||||||
|
|
||||||
function result(obj)
|
function result(obj)
|
||||||
std.custom_header("Connection","close")
|
|
||||||
std.json()
|
std.json()
|
||||||
std.t(JSON.encode({result=obj, error=false}))
|
std.t(JSON.encode({result=obj, error=false}))
|
||||||
end
|
end
|
||||||
@ -29,33 +26,17 @@ function sysdb()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function is_auth()
|
function is_auth()
|
||||||
local sessionid = nil
|
if SESSION.sessionid == nil or SESSION.sessionid == '0' then return false end
|
||||||
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
|
|
||||||
-- query session id from database
|
-- query session id from database
|
||||||
local db = sysdb()
|
local db = sysdb()
|
||||||
if db == nil then return false end
|
if db == nil then return false end
|
||||||
local cond = {exp= {["="] = { sessionid = sessionid }}}
|
local cond = {exp= {["="] = { sessionid = SESSION.sessionid }}}
|
||||||
local data = db:find(cond)
|
local data = db:find(cond)
|
||||||
--print(JSON.encode(data))
|
--print(JSON.encode(data))
|
||||||
db:close()
|
db:close()
|
||||||
if data == nil or data[1] == nil then return die("No user data found") end
|
if data == nil or data[1] == nil then return die("No user data found") end
|
||||||
-- next time check the stamp
|
-- next time check the stamp
|
||||||
SESSION.user = data[1].username
|
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
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -63,4 +44,4 @@ function auth_or_die(msg)
|
|||||||
if(is_auth() == false) then
|
if(is_auth() == false) then
|
||||||
die(msg)
|
die(msg)
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -113,7 +113,7 @@ vfs.write = function(path,data)
|
|||||||
local uid = ulib.uid(SESSION.user)
|
local uid = ulib.uid(SESSION.user)
|
||||||
--
|
--
|
||||||
if data ~= "" then
|
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
|
if header ~= nil then
|
||||||
local b64data = string.gsub(data, utils.escape_pattern(header),"")
|
local b64data = string.gsub(data, utils.escape_pattern(header),"")
|
||||||
local barr = std.b64decode(b64data)
|
local barr = std.b64decode(b64data)
|
||||||
@ -141,23 +141,13 @@ vfs.write = function(path,data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
vfs.upload = function(path)
|
vfs.upload = function(path)
|
||||||
if(not path) then
|
|
||||||
return false, "Unknown upload destination, abort!"
|
|
||||||
end
|
|
||||||
local r,m = vfs.checkperm(path,"write")
|
local r,m = vfs.checkperm(path,"write")
|
||||||
if(r) then
|
if(r) then
|
||||||
local uid = ulib.uid(SESSION.user)
|
local uid = ulib.uid(SESSION.user)
|
||||||
local index = 0
|
local file = m.."/"..REQUEST["upload.file"]
|
||||||
while(REQUEST["upload-"..index..".tmp"] ~= nil) do
|
ulib.move(REQUEST["upload.tmp"], file)
|
||||||
local file = m.."/"..REQUEST["upload-"..index..".file"]
|
ulib.chown(file, uid.id, uid.gid)
|
||||||
ulib.move(REQUEST["upload-"..index..".tmp"], file)
|
return true, nil
|
||||||
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
|
|
||||||
else
|
else
|
||||||
return r,m
|
return r,m
|
||||||
end
|
end
|
||||||
@ -166,14 +156,14 @@ end
|
|||||||
vfs.checkperm = function(path, right)
|
vfs.checkperm = function(path, right)
|
||||||
local osfile = vfs.ospath(path)
|
local osfile = vfs.ospath(path)
|
||||||
local perm = vfs.perm(osfile)
|
local perm = vfs.perm(osfile)
|
||||||
--print(osfile)
|
print(osfile)
|
||||||
if not ulib.exists(osfile) then
|
if not ulib.exists(osfile) then
|
||||||
return false,"Resource does not exist"
|
return false,"Resource does not exist"
|
||||||
end
|
end
|
||||||
-- check if user own the file
|
-- check if user own the file
|
||||||
if perm ~= nil then
|
if perm ~= nil then
|
||||||
if perm[right] == true then
|
if perm[right] == true then
|
||||||
--print("Permission granted")
|
print("Permission granted")
|
||||||
return true,osfile
|
return true,osfile
|
||||||
else
|
else
|
||||||
print("Permission denie")
|
print("Permission denie")
|
||||||
@ -192,13 +182,13 @@ vfs.perm = function(file)
|
|||||||
if uid ~= nil and st ~= nil and st.perm ~= nil then
|
if uid ~= nil and st ~= nil and st.perm ~= nil then
|
||||||
--print(JSON.encode({uid, st}))
|
--print(JSON.encode({uid, st}))
|
||||||
if(uid.id == st.uid) then -- the user owned the file
|
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
|
return st.perm.owner
|
||||||
elseif uid.groups and uid.groups[st.gid] then
|
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
|
return st.perm.group
|
||||||
else
|
else
|
||||||
--print("User belong to other")
|
print("User belong to other")
|
||||||
return st.perm.other
|
return st.perm.other
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
Reference in New Issue
Block a user