gemini/server.ts

400 lines
13 KiB
TypeScript

console.log(`
_ _ _ _
| |_____| |____ __ __ ____ _| |_ ___ ___ _ _ ___| |_
| / / -_) / /\ V V /(_-< || | _/ -_|_-<_| ' \/ -_) _|
|_\_\___|_\_\ \_/\_(_)__/\_, |\__\___/__(_)_||_\___|\__|
|__/
`);
const gitdir = "/home/"
import { existsSync,readFileSync, writeFileSync,mkdirSync,unlinkSync,rmSync,readdirSync} from "fs";
import gemini, { Request, Response, TitanRequest, NextFunction } from "gemini-server";
import { Sequelize, DataTypes } from "sequelize";
process.title = "gemini-server"
const {gitToJs} = require("git-parse")
const options = {
cert: readFileSync("cert.pem"),
key: readFileSync("key.pem"),
titanEnabled: true,
};
try{
mkdirSync("tmp")
//mkdirSync("git")
}catch(e){}
const app = gemini(options);
let git = readdirSync(gitdir+"git")
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'sql/forums.sqlite',
logging: false
});
const forums = sequelize.define('forums', {
topic: {
type: DataTypes.STRING,
allowNull: false
},
created: {
type: DataTypes.STRING,
allowNull: false
},
thread:{
type: DataTypes.STRING,
allowNull: false,
}
}, {});
//make sure it exists
(async()=>{
sequelize.sync();
/*
await sequelize.sync({force:true}); // clear table
let thread2 = await forums.create({topic:"test2",created:"time",thread:[]})
thread2.set({
topic:"test33",
thread:[{
"hello":"world",
"main":"test4",
"comments":[]
},],
})
thread2.save()
sequelize.sync({alter:true})
*/
})()
function datefmt (date:any, fstr:any, utc:any) {
utc = utc ? 'getUTC' : 'get';
return fstr.replace (/%[YmdHMS]/g, function (m:any) {
switch (m) {
case '%Y': return date[utc + 'FullYear'] (); // no leading zeros required
case '%m': m = 1 + date[utc + 'Month'] (); break;
case '%d': m = date[utc + 'Date'] (); break;
case '%H': m = date[utc + 'Hours'] (); break;
case '%M': m = date[utc + 'Minutes'] (); break;
case '%S': m = date[utc + 'Seconds'] (); break;
default: return m.slice (1); // unknown code, remove %
}
// add leading zero if required
return ('0' + m).slice (-2);
});
}
app.use(async(_req: Request, res: Response) => {
if(_req.url.pathname.includes('/git')){
let gitlist = ''
for(let g of git){
gitlist += "=> gemini://"+_req.url.host+"/git/"+g+" "+g+"\n"
}
if(_req.url.pathname=='/git'||_req.url.pathname=='/git/'){
let a:any = Math.random()
let t = readFileSync("./pages/git.gemini").toString().replace(/{git-list}/g,gitlist).replace(/{domain}/g,_req.url.host)
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
return;
} else {
let reqd = ''
for(let g of git){
if(_req.url.pathname.split("/")[2].includes(g)||_req.url.pathname.split("/")[2].includes(g+"/")){
reqd = g
break
}
}
if(reqd==''){
let a:any = Math.random()
let t = readFileSync("./pages/404.gemini").toString().replace(/{domain}/g,_req.url.host)
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
return
} else {
if(_req.url.pathname.split("/")[3]==undefined){
let parsedGit
try{
parsedGit = await gitToJs(gitdir+_req.url.pathname)
} catch (e){
res.data("not a valid git page")
return;
}
let a:any = Math.random()
let cList = ''
for(let commit of parsedGit){
cList +=`\n=> gemini://${_req.url.host}${_req.url.pathname}/${commit.hash} ${commit.authorName} ${commit.hash.substring(0, 9)}... "${commit.message}" ${commit.date} `
for(let i of commit.filesAdded){
cList += '+'
}
for(let i of commit.filesDeleted){
cList += '-'
}
for(let i of commit.filesModified){
cList += '~'
}
for(let i of commit.filesRenamed){
cList += '~'
}
}
let readme = ''
if(existsSync(gitdir+_req.url.pathname+'/readme')){
readme = '\n\n## readme\n\n'+readFileSync(gitdir+_req.url.pathname+'/readme').toString()
}
if(existsSync(gitdir+_req.url.pathname+'/readme.md')){
readme = '\n\n## readme\n\n'+readFileSync(gitdir+_req.url.pathname+'/readme.md').toString()
}
let t = `=> gemini://${_req.url.host+_req.url.pathname}/source source\n\n#${reqd}${readme}\n\n## commits\n${cList}`
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
} else {
let x = (_req.url.pathname.split('/'))
let z = x.splice(3,1)
let parsedGit
try{
parsedGit = await gitToJs(gitdir+x.join('/'))
} catch(e){
parsedGit = []
}
let a:any = Math.random()
let cList:any = []
for(let commit of parsedGit){
if(z == commit.hash){
let t = `# commit ${commit.hash}\n\n`
for(let i of commit.filesAdded){
i['action']='add'
cList.push(i)
}
for(let i of commit.filesDeleted){
i['action']='del'
cList.push(i)
}
for(let i of commit.filesModified){
i['action']='mod'
cList.push(i)
}
for(let i of commit.filesRenamed){
i['action']='mv'
cList.push(i)
}
for(let ch of cList){
if(ch.action=='add'){
t+=`+${ch.path}\n`
}
if(ch.action=='del'){
t+=`-${ch.path}\n`
}
if(ch.action=='mod'){
t+=`~${ch.path}\n`
}
if(ch.action=='mv'){
t+=`${ch.oldPath}->${ch.newPath}\n`
}
}
t+=`\n## info\n\nauthor - ${commit.authorName} (${commit.authorEmail})\ndate - ${commit.date}\nmessage - ${commit.message}`
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
return
}
}
x = (_req.url.pathname.split('/'))
z = x.splice(3,1)
let def = x
let aaa = '';
for(let l of def){
aaa+=l+"/"
}
let aa = aaa.split('/')
aa.splice(3,0,z.join(""))
let bbb = '';
for(let l of def){
bbb+=l+"/"
}
let bb = bbb.split('/')
for(let n of bb){
if(n==''){
bb.splice(bb.indexOf(n),1)
}
}
if(!(bb.length==2)){
bb.splice(bb.length-1,1)
if(!bb.includes("source")){
bb.splice(2,0,"source")
}
}
//bb.splice(bb.length,1)
if(z.join("").includes('source')){
var ignore:string[];
try{
let gitDir = readdirSync(gitdir+x.join("/"))
try{
ignore = readFileSync(gitdir+x.join("/")+"/.gitignore").toString().split("\n")
} catch(e){
ignore = [];
}
//let t = `=> gemini://${_req.url.host+x.join('/')} ${x.join('/')}\n\n`
let t = `=> gemini://${_req.url.host}${_req.url.pathname} [d] .\n=> gemini://${_req.url.host}/${bb.join("/") } [d] ..\n`
for(let i of gitDir){
if(!ignore.includes(i)){
try{
//! may try to append an extra "/" (maybe?)
readFileSync(gitdir+x.join("/")+"/"+i)
t += `=> gemini://${_req.url.host+aa.join("/")+i} ${i}\n`
} catch(e) {//is directory
t += `=> gemini://${_req.url.host+aa.join("/")+i} [d] ${i}\n`
}
}
}
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
return
}catch (e) {//a file
try{
let a:any = Math.random()
let f = (readFileSync(gitdir+x.join("/")).toString())
writeFileSync("tmp/"+a+".gemini",f)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
return
} catch(e){
let t = readFileSync("./pages/404.gemini").toString().replace(/{domain}/g,_req.url.host)
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
return
}
}
}
let t = readFileSync("./pages/404.gemini").toString().replace(/{domain}/g,_req.url.host)
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
return
}
}
}
} else if(_req.url.pathname.includes('/forums')) {
if(_req.url.pathname=="/forums"){
let a:any = Math.random()
let topics = await forums.findAll()
let top = ''
for(let topic of topics){
top+=`=> gemini://${_req.url.host}/${_req.url.pathname}/${topic.getDataValue('id')} ${topic.getDataValue('topic')}\n`
}
let t= readFileSync("./pages/forums.gemini").toString().replace(/{domain}/g,_req.url.host).replace(/{forums}/g,top)
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
} else if(_req.url.pathname=="/forums/submissive"){
if(_req.query!=null){
_req.query=_req.query+''
_req.query=decodeURI(_req.query)
let thread = await forums.create({topic:_req.query,created:datefmt(new Date(),"%Y-%m-%d %H:%M:%S", true),thread:"[]"})
thread.save()
sequelize.sync({alter:true})
res.redirect(thread.getDataValue('id')+'')
return
} else {
res.input("forum name")
return
}
} else {
let foru = _req.url.pathname.split('/forums/')[1]
//console.log(foru)
let all = await forums.findAll()
let id = parseInt(foru)
//for()
// // @ts-ignore
for(let a of all){
if(a.getDataValue('id')==id){
if(foru.split('/').length>=2){
//extra command TwT
if(_req.query!=null){
_req.query=_req.query+''
_req.query=decodeURI(_req.query)
let parse = (JSON.parse(a.getDataValue('thread')))
parse[parse.length]={m:_req.query,date:datefmt(new Date(),"%Y-%m-%d %H:%M:%S", true)}
a.setDataValue('thread',JSON.stringify(parse))
a.save()
sequelize.sync({alter:true})
viewForum(res,_req,a)
return
} else {
res.input("comment")
return
}
} else {
viewForum(res,_req,a)
}
return
}
}
// !404
}
}else {
let a:any = Math.random()
let t = readFileSync("./pages/404.gemini").toString().replace(/{domain}/g,_req.url.host)
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
}
});
function viewForum(res:any,_req:any,a:any){
let z = Math.random()
//console.log(a.getDataValue('thread'))
let com = ''
for(let c of (JSON.parse(a.getDataValue('thread')))){
com+=`\n### ${c.date}\n\n${c.m}\n__________`
}
let s = (`# ${a.dataValues.topic}\n\nlast - ${a.dataValues.createdAt}\nupdated - ${a.dataValues.updatedAt}\n\n=> gemini://${_req.url.host}${_req.url.pathname}/sub comment\
\n\n## thread\n\n${com}`)
writeFileSync("tmp/"+z+".gemini",s)
res.file("tmp/"+z+".gemini");
unlinkSync("tmp/"+z+".gemini")
}
app.on("/", (_req: Request, res: Response) => {
let a:any = Math.random()
let t = readFileSync("./pages/index.gemini").toString().replace(/{domain}/g,_req.url.host)
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
});
app.on("/aboutme", (_req: Request, res: Response) => {
let a:any = Math.random()
let t = readFileSync("./pages/aboutme.gemini").toString().replace(/{domain}/g,_req.url.host)
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
});
app.on("/ascii", (_req: Request, res:Response) => {
let a:any = Math.random()
let t = readFileSync("./pages/ascii.gemini").toString().replace(/{domain}/g,_req.url.host)
writeFileSync("tmp/"+a+".gemini",t)
res.file("tmp/"+a+".gemini");
unlinkSync("tmp/"+a+".gemini")
})
app.listen(() => {
console.log("Listening...");
});
process.on('exit', function (code) {
rmSync("tmp", { recursive: true, force: true })
});