This commit is contained in:
pietru 2024-06-24 23:13:08 +02:00
commit e7b8195788
32 changed files with 22368 additions and 0 deletions

22
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,22 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Run tests",
"type": "shell",
"windows": {
"command": ".\\run.bat"
},
"linux": {
"command": "./run.sh"
},
"group": "none",
"presentation": {
"reveal": "always",
"panel": "shared"
}
}
]
}

1059
CHANGELOG.md Normal file

File diff suppressed because it is too large Load diff

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 pietru
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

17
PocketBase-LICENSE.md Normal file
View file

@ -0,0 +1,17 @@
The MIT License (MIT)
Copyright (c) 2022 - present, Gani Georgiev
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

16
docker-compose.yml Normal file
View file

@ -0,0 +1,16 @@
version: '2'
services:
db:
build: .
tty: true
stdin_open: true
environment:
- TimeZone=Europe/Warsaw # Optional - Change Timezone
volumes:
- ./pb_data:/pb/pb_data
- ./pb_hooks:/pb/pb_hooks
#For production you might want to copy pb_hooks directly into image.
#Some hosting providers might not allow you to dynamicaly change pb_hooks.
ports:
- 8090:8090
restart: unless-stopped

22
dockerfile.yml Normal file
View file

@ -0,0 +1,22 @@
FROM alpine:latest
ARG PB_VERSION=0.22.14
RUN apk add --no-cache \
unzip \
ca-certificates
# download and unzip PocketBase
ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip /tmp/pb.zip
RUN unzip /tmp/pb.zip -d /pb/
# uncomment to copy the local pb_migrations dir into the image
# COPY ./pb_migrations /pb/pb_migrations
# uncomment to copy the local pb_hooks dir into the image
# COPY ./pb_hooks /pb/pb_hooks
EXPOSE 8090
# start PocketBase
CMD ["/pb/pocketbase", "serve", "--http=0.0.0.0:8090"]

BIN
pb_data/data.db Normal file

Binary file not shown.

BIN
pb_data/logs.db Normal file

Binary file not shown.

View file

@ -0,0 +1,16 @@
version: '2'
services:
db:
build: .
tty: true
stdin_open: true
environment:
- TimeZone=Europe/Warsaw # Optional - Change Timezone
volumes:
- ./pb_data:/pb/pb_data
- ./pb_hooks:/pb/pb_hooks
#For production you might want to copy pb_hooks directly into image.
#Some hosting providers might not allow you to dynamicaly change pb_hooks.
ports:
- 8090:8090
restart: unless-stopped

View file

@ -0,0 +1 @@
{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":{"original-filename":"docker-compose.yml"},"md5":"sUke1Dg2Lw/zzeneNm9LkQ=="}

View file

@ -0,0 +1,22 @@
FROM alpine:latest
ARG PB_VERSION=0.22.14
RUN apk add --no-cache \
unzip \
ca-certificates
# download and unzip PocketBase
ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip /tmp/pb.zip
RUN unzip /tmp/pb.zip -d /pb/
# uncomment to copy the local pb_migrations dir into the image
# COPY ./pb_migrations /pb/pb_migrations
# uncomment to copy the local pb_hooks dir into the image
# COPY ./pb_hooks /pb/pb_hooks
EXPOSE 8090
# start PocketBase
CMD ["/pb/pocketbase", "serve", "--http=0.0.0.0:8090"]

View file

@ -0,0 +1 @@
{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":{"original-filename":"dockerfile.yml"},"md5":"f3oK8Nld+tNZabBQUsSwFA=="}

20585
pb_data/types.d.ts vendored Normal file

File diff suppressed because it is too large Load diff

71
pb_hooks/file.pb.js Normal file
View file

@ -0,0 +1,71 @@
/// <reference path="../pb_data/types.d.ts" />
routerAdd("post", "/file", (c) => {
const collection_name = "files"
const data = $apis.requestInfo(c).data
console.log(JSON.stringify(data));
if (data["filename"]==null){
return c.redirect(307, "/error");
}
let filename = data["filename"];
filename = filename.replace(/'/g, "");
let passwd = "";
if (data["passwd"]!=null){
passwd = String(data["passwd"])
}
passwd = passwd.replace(/'/g, "");
let tittle = ""
let desc = ""
let password = ""
const file_record = new Record();
try {
$app.dao().recordQuery(collection_name)
.andWhere($dbx.hashExp({
"filename": filename,
}))
.limit(1)
.one(file_record)
console.log(JSON.stringify(file_record));
if (file_record){
tittle=file_record.get("tittle")
desc=file_record.get("desc")
password=file_record.get("password")
}
} catch (error) {
vid_desc="File does not exist"
const utils = require(`${__hooks}/utils.js`)
return c.html(404, utils.get_error_page(404,"File not found..."))
}
const recID = file_record.get("id")
let links = []
file_record.get("data").forEach(e => {
links.push({
"name":e,
"link":`/api/files/${collection_name}/${recID}/${e}`
})
});
console.log(JSON.stringify(file_record.get("data")))
console.log(JSON.stringify(links))
const html = $template.loadFiles(
`${__hooks}/views/base/layout.html`,
`${__hooks}/views/base/header.html`,
`${__hooks}/views/file.html`,
).render({
"filename":filename,
"tittle":tittle,
"desc":desc,
"links":links,
"authorized":passwd==password,
})
return c.html(200, html)
})
routerAdd("get", "/file", (c) => {
return c.redirect(307, "/error");
})

23
pb_hooks/main.pb.js Normal file
View file

@ -0,0 +1,23 @@
/// <reference path="../pb_data/types.d.ts" />
//app created on 25.09.2023
routerAdd("get", "/", (c) => {
const html = $template.loadFiles(
`${__hooks}/views/base/layout.html`,
`${__hooks}/views/base/header.html`,
`${__hooks}/views/base/footer.html`,
`${__hooks}/views/main.html`,
).render({})
return c.html(200, html)
})
routerAdd("get","/error",(c) => {
const utils = require(`${__hooks}/utils.js`)
return c.html(404, utils.get_error_page(404,`Unknown Error`))
}, $apis.activityLogger($app))
routerAdd("post","/error",(c) => {
const utils = require(`${__hooks}/utils.js`)
return c.html(404, utils.get_error_page(404,`Unknown Error`))
}, $apis.activityLogger($app))

15
pb_hooks/utils.js Normal file
View file

@ -0,0 +1,15 @@
module.exports = {
get_error_page: (code,msg,auth=false) => {
const html = $template.loadFiles(
`${__hooks}/views/base/layout.html`,
`${__hooks}/views/base/header.html`,
`${__hooks}/views/base/footer.html`,
`${__hooks}/views/error.html`,
).render({
"code":code,
"msg":msg,
"authorized":auth,
})
return html
}
}

View file

@ -0,0 +1,16 @@
{{define "footer"}}
<style>
footer {
max-width: 512px;
background-color: bisque;
border-radius: 5px 5px 0px 0px;
margin-top: 20px;
padding: 2px 5px 5px;
}
</style>
<center>
<footer>
Author: pietru | 2023
</footer>
</center>
{{end}}

View file

@ -0,0 +1,34 @@
{{define "header"}}
<style>
nav {
max-width: 512px;
background-color: rgba(255, 228, 196, 0);
border-radius: 0px 0px 5px 5px;
padding: 2px 5px 5px;
}
header {
margin-bottom: 20px;
}
.headnav:link, .headnav:visited {
background-color: white;
color: black;
border: 2px solid chocolate;
padding: 5px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
}
.headnav:hover, .headnav:active {
background-color: chocolate;
color: white;
}
</style>
<header>
<center>
<nav>
<a class="headnav" href="/">Home</a>
</nav>
</center>
</header>
{{end}}

View file

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{block "title" .}}Default app title{{end}}</title>
<style>
.btn1:link, .btn1:visited {
background-color: white;
color: black;
border: 2px solid rgb(30, 174, 210);
padding: 5px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
}
.btn1:hover, .btn1:active {
background-color: rgb(20, 122, 148);
color: white;
}
.btn2:link, .btn2:visited {
background-color: white;
color: black;
border: 2px solid rgb(30, 114, 210);
padding: 5px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
}
.btn2:hover, .btn2:active {
background-color: rgb(24, 92, 170);
color: white;
}
</style>
</head>
<body style="background-color: gray; margin: 0px 8px;">
{{block "header" .}}
Default app header
{{end}}
<main style="min-height: calc(100vh - 40px);">
{{block "body" .}}
Default app body...
{{end}}
</main>
{{block "footer" .}}
{{end}}
</body>
</html>

24
pb_hooks/views/error.html Normal file
View file

@ -0,0 +1,24 @@
{{define "title"}}
Page 1
{{end}}
{{define "body"}}
<style>
section {
background-color: white;
border-radius: 10px;
max-width: 512px;
padding: 5px;
text-align: left;
}
p, h2 {
margin: 10px 10px;
}
</style>
<center>
<section>
<h2>Error {{.code}}</h2>
<p>{{.msg}}</p>
</section>
</center>
{{end}}

63
pb_hooks/views/file.html Normal file
View file

@ -0,0 +1,63 @@
{{define "title"}}
{{.vid_tittle}}
{{end}}
{{define "body"}}
<style>
.grid-container {
display: grid;
grid-template-columns: auto;
align-items: center;
text-align: center;
}
@media only screen and (min-width: 400px) {
.grid-container {grid-template-columns: auto auto;}
}
section {
background-color: white;
border-radius: 10px;
max-width: 700px;
padding: 5px;
text-align: left;
}
p, h2 {
margin: 10px 10px;
}
</style>
<center>
<section>
{{if .authorized}}
<a class="btn2" style="float:right;" href="/">Return</a>
<h2>{{.tittle}}</h2>
<p>{{.desc|raw}}</p>
<center>
{{range $p := .links}}
<br>
{{$p.name}}
<span> | </span>
<a href="{{$p.link}}" class="btn1">download</a>
{{end}}
</center>
{{else}}
<center>
<a class="headnav" href="/">Return</a>
</center>
<br>
<form class="grid-container" action="/file" method="post">
<label for="filename">File:</label>
<input type="text" name="filename" id="filename" placeholder="File Name" required readonly value="{{.filename}}">
<label for="passwd">Password:</label>
<input type="password" name="passwd" id="passwd" required placeholder="File Password">
<center style="grid-column: 1 / 3;">
<button type="submit">Submit</button>
</center>
</form>
{{end}}
</section>
</center>
{{end}}

12
pb_hooks/views/hello.html Normal file
View file

@ -0,0 +1,12 @@
{{define "title"}}
Page 1
{{end}}
{{define "body"}}
<p>Hello from {{.name}}</p>
{{range $p := .posts}}
<p id="twt">{{$p}}<br></p>
{{end}}
{{end}}

60
pb_hooks/views/main.html Normal file
View file

@ -0,0 +1,60 @@
{{define "title"}}
{{.tittle}}
{{end}}
{{define "body"}}
<style>
img {
width: 100% !important;
max-width: 300px;
height: auto !important;
}
section {
background-color: white;
border-radius: 10px;
max-width: 400px;
padding: 5px;
}
.middle {
display: grid;
}
p, h2 {
margin: 10px 10px;
}
.grid-container {
display: grid;
grid-template-columns: auto;
align-items: center;
text-align: center;
}
@media only screen and (min-width: 700px) {
.grid-container {grid-template-columns: auto auto;}
}
.grid-item {
border: 2px solid black;
border-radius: 5px;
text-align: center;
}
a:link {
text-decoration: none;
}
input::placeholder {
font-size: 18px;
}
</style>
<center>
<section style="font-size: 25px;">
<h2>Please input file data.</h2>
<br>
<form class="grid-container" action="/file" method="post">
<label for="filename">File:</label>
<input type="text" name="filename" id="filename" placeholder="File Name" required>
<button type="submit" style="grid-column: 1 / 3;">Submit</button>
</form>
</section>
</center>
{{end}}

View file

@ -0,0 +1,85 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const collection = new Collection({
"id": "u6nunqddkjhg8kw",
"created": "2024-06-24 19:39:10.790Z",
"updated": "2024-06-24 19:39:10.790Z",
"name": "files",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "1mlvuysb",
"name": "filename",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "tl5hk5kt",
"name": "field",
"type": "file",
"required": false,
"presentable": false,
"unique": false,
"options": {
"mimeTypes": [],
"thumbs": [],
"maxSelect": 99,
"maxSize": 5242880,
"protected": true
}
},
{
"system": false,
"id": "qnsplmik",
"name": "tittle",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "urnzf193",
"name": "desc",
"type": "editor",
"required": false,
"presentable": false,
"unique": false,
"options": {
"convertUrls": false
}
}
],
"indexes": [
"CREATE UNIQUE INDEX `idx_tj3SJDs` ON `files` (`filename`)"
],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
});
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("u6nunqddkjhg8kw");
return dao.deleteCollection(collection);
})

View file

@ -0,0 +1,48 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("u6nunqddkjhg8kw")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "tl5hk5kt",
"name": "data",
"type": "file",
"required": false,
"presentable": false,
"unique": false,
"options": {
"mimeTypes": [],
"thumbs": [],
"maxSelect": 99,
"maxSize": 5242880,
"protected": true
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("u6nunqddkjhg8kw")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "tl5hk5kt",
"name": "field",
"type": "file",
"required": false,
"presentable": false,
"unique": false,
"options": {
"mimeTypes": [],
"thumbs": [],
"maxSelect": 99,
"maxSize": 5242880,
"protected": true
}
}))
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,31 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("u6nunqddkjhg8kw")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "k5p1rids",
"name": "password",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("u6nunqddkjhg8kw")
// remove
collection.schema.removeField("k5p1rids")
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,16 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("u6nunqddkjhg8kw")
collection.viewRule = ""
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("u6nunqddkjhg8kw")
collection.viewRule = null
return dao.saveCollection(collection)
})

BIN
pocketbase Normal file

Binary file not shown.

BIN
pocketbase.exe Normal file

Binary file not shown.

34
readme.md Normal file
View file

@ -0,0 +1,34 @@
# File DB App
##### _Small app for sharing files with people_
## Features
- Main Page for entering file phrase
- Ability to have password on files (separate code/phrase from filename)
- Ability to view file entries with tittle and desc and download links (use pocketbase admin ui to add files)
## Installation
Default Login: local@localhost.local
Default Password: localhost1234
#### Manual install
Download Project
Unzip into folder
Run command:
- On Windows: .\pocketbase.exe serve
- On Linux: ./pocketbase serve
#### Docker compose
Download Project
Unzip into folder
Run command: docker-compose up -d
## License
MIT with one small change:
Commercial Use Allowed if you:
- Change Database Admin Email
- Change Database Admin Password

1
run.bat Normal file
View file

@ -0,0 +1 @@
.\pocketbase.exe serve --dev

1
run.sh Normal file
View file

@ -0,0 +1 @@
./pocketbase serve --dev