Initialized project

This is bad
This commit is contained in:
Xodrium 2023-02-14 00:30:10 -05:00
parent a753a5800f
commit 21ccd4983e
28 changed files with 1125 additions and 0 deletions

11
.gitignore vendored Normal file
View file

@ -0,0 +1,11 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
/db

0
README.md Normal file
View file

23
package.json Normal file
View file

@ -0,0 +1,23 @@
{
"name": "sanifae",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^1.0.0",
"@sveltejs/kit": "^1.0.0",
"svelte": "^3.54.0",
"vite": "^4.0.0"
},
"type": "module",
"dependencies": {
"@sveltejs/adapter-node": "^1.1.4",
"bcrypt": "^5.1.0",
"sqlite": "^4.1.2",
"sqlite3": "^5.1.4"
}
}

15
src/app.html Normal file
View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/icon_sanifae_black.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap" rel="stylesheet">
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover" style="margin: 0; padding: 0; overflow: hidden;">
<div>%sveltekit.body%</div>
</body>
</html>

View file

@ -0,0 +1,71 @@
<style>
#content {
background: var(--light-1);
box-shadow: 0px 3px 5px 3px var(--dark-2);
width: min(700px, 90vw);
padding-top: 8px;
margin: 25px;
border-radius: 25px;
display: flex;
flex-direction: column;
}
#content > div {
padding: 15px;
}
#header {
font-weight: bold;
font-size: 2rem;
width: 100%;
padding: 0;
}
#header {
border-bottom: var(--dark-2) solid 2px;
width: calc(100% - 30px);
}
#main {
min-height: 250px;
max-height: 500px;
overflow-y: scroll;
}
#main.tiny {
min-height: initial;
height: 100px;
overflow: hidden;
}
</style>
<script>
export let tiny = false;
let form;
</script>
<div id='content'>
<div id='header'>
<slot name="header">
Header
</slot>
</div>
<div id='main' class='{tiny ? "tiny" : ""}'>
<slot name="main">
<p>Content</p>
</slot>
</div>
<div id='footer'>
<slot name="footer">
<p>Footer</p>
</slot>
</div>
</div>

View file

@ -0,0 +1,28 @@
<style>
.button {
padding: 0.5rem;
background-color: var(--dark-3);
border: none;
font-family: 'Open Sans';
border-radius: 2rem;
margin: 0.2rem;
}
.button a {
color: var(--light-1);
font-weight: bold;
}
</style>
<script>
export let clickFunc = () => {};
export let button = '';
</script>
<button on:click={clickFunc} class='button'>
<a href='#'><slot/></a>
</button>

View file

@ -0,0 +1,80 @@
<script>
import Button from '$lib/components/Button.svelte';
export let form;
let fileInput;
let files;
let preview;
let progress = 0;
function getBase64(image) {
const reader = new FileReader();
reader.readAsDataURL(image);
reader.onload = e => {
preview = e.target.result;
uploadFunction(e.target.result);
};
};
async function uploadFunction(imageD) {
const imgData = imageD.split(',').pop();
var images = [];
var fSize = 1024 * 256;
for (var i = 0; i * fSize < imgData.length + 1; i++) {
images.push(imgData.substring(i * fSize, i * fSize + fSize));
}
var rId = (Math.random() + 1).toString(36).substring(7);;
var extension = fileInput.value.split('.').pop();
for (var i = 0; i < images.length; i++) {
var fData = (new FormData());
var image = images[i];
fData.append('img',image);
fData.append('extension',extension);
fData.append('last',i == (images.length - 1));
fData.append('id',rId);
form = await fetch(`/api/fileCreate`, {
method: 'POST',
body: fData,
}).then(x => x.json());
progress = (i+1) / (images.length);
await setTimeout(() => new Promise.resolve(),100);
}
};
</script>
<style>
.hidden {
display: none;
}
img {
max-width: 250px;
}
</style>
<form action='#' method='GET' >
{#if preview}
<img src={preview} alt="Image preview"/>
{:else}
<img src='/YourOC.svg' alt="Image preview"/>
{/if}
<input class="hidden" id="file-to-upload" type="file" bind:files bind:this={fileInput} on:change={() => getBase64(files[0])}/>
<p>
Progress: {progress * 100}%
</p>
<p>
<Button class="upload-btn" clickFunc={ () => fileInput.click() }>Upload</Button>
</p>
</form>

View file

@ -0,0 +1,58 @@
<script>
export let data = '';
</script>
<style>
#logo {
width: calc(100vw - 20px);
background: var(--dark-1);
padding: 5px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
position: sticky;
top: 0;
}
#logo a {
padding: 10px;
color: var(--light-1);
font-weight: bold;
}
#logo img {
height: 30px;
width: auto;
display: block;
}
</style>
<div id='logo'>
<a href='/'>
<img src='/icon_sanifae.svg' alt='Sanifae Logo'>
</a>
{#if data.username && data.username != 'false'}
<a href='/user/{data.username}'>
{data.username}
</a>
<a href='/logout'>
Log out
</a>
<a href='/new_post'>
Create
</a>
{:else}
<a href='/account'>
Log in / Register
</a>
{/if}
</div>

95
src/lib/db/db.js Normal file
View file

@ -0,0 +1,95 @@
const FILE_SIZE_LIMIT = 1024*1024*16;
const VALID_EXTENSIONS = ['png','jpg','jpeg','svg'];
var ridArray = {};
import sqlite3 from 'sqlite3'
import { writeFile } from 'node:fs/promises';
import { safePath, user } from '../util.js';
import { open } from 'sqlite'
import { createHash } from 'node:crypto';
var db;
async function initDb() {
db = await open({
filename: `${process.cwd()}/db/main.db`,
driver: sqlite3.Database
});
await db.run('CREATE TABLE IF NOT EXISTS rating (id CHAR(256), rating INTEGER)');
}
let backendProxy = async ({route, backendParams,}) => {
if (!db) await initDb();
const res = await fetch(`https://sanifae.insfa.net/api/token`,{
'method': 'GET', 'headers': {
'cookie': backendParams.cookie
}
});
backendParams['admin'] = ((await res.json()).data == user);
return backend[route](backendParams);
}
var backend = {};
backend.rateOc = async ({admin,id,rating}) => {
if (admin) {
await db.run('UPDATE rating SET rating = ? WHERE id = ?', [
rating,
id
])
}
};
backend.oc = async ({admin}) => {
return await db.all('SELECT * FROM rating WHERE rating > ? ORDER BY rating DESC', [
(admin) ? -1000 : -1
]);
}
backend.fileCreate = async({img, extension,id, last }) => {
if (ridArray[id] !== '' && !(ridArray[id])) {
ridArray[id] = img;
} else {
ridArray[id] += img;
}
const imgData = ridArray[id];
if (last != 'true') {
return {'success': 'Image still proccessing...'}
} else {
ridArray[id] = false;
}
const imgHash = createHash('md5').update(imgData).digest('hex');
if (!imgHash)
return {'success': 'Image not provided.'}
if (imgHash.length > FILE_SIZE_LIMIT)
return {'success': 'Image too big.'}
const extensionSafe = safePath(extension);
if (VALID_EXTENSIONS.indexOf(extensionSafe) == -1)
return { success: 'Illegal file extension. Permitted file extensions are: ' + VALID_EXTENSIONS.join(', ') };
writeFile(`${process.cwd()}/db/post-${imgHash}.${extensionSafe}`,imgData,{encoding: 'base64'});
await db.run('INSERT INTO rating (id,rating) VALUES (?,?)', [
`${imgHash}.${extensionSafe}`,
-1
]);
return { success: 'Successfully uploaded file.', 'href': `/img/${imgHash}.${extensionSafe}`};
}
export {
backendProxy,
backend
}

85
src/lib/util.js Normal file
View file

@ -0,0 +1,85 @@
const EXTENSION_MAP = {
'png': 'img',
'jpg': 'img',
'jpeg': 'img',
'gif': 'img',
'svg': 'img',
'mp4': 'video'
}
let checkLength = function(string, field, lowerBound, upperBound) {
if (string.length < lowerBound) {
if (string.length == 0) {
return { success: `${field} cannot be blank.` }
}
return { success: `${field} is too short (minimum length: ${lowerBound} characters).` }
}
if (string.length > upperBound) {
return { success: `${field} is too long (maximum length: ${upperBound} characters).` }
}
return false;
}
let checkRegex = function(string, field, regex) {
if (string.search(regex) != -1) {
return { success: `${field} contains illegal characters.` }
}
return false;
}
let handleSubmitGet = async e => {
const ACTION_URL = e.target.action
const formData = new FormData(e.target);
const asString = new URLSearchParams(formData).toString();
return await fetch(ACTION_URL + '?' + asString, {
method: 'GET',
}).then(x => x.text());
}
let handleSubmit = async e => {
const ACTION_URL = e.target.action
const formData = new FormData(e.target);
return await fetch(ACTION_URL, {
method: 'POST',
body: formData
}).then(x => x.text());
}
let safeName = function (text) {
return text.replaceAll(/[^A-Za-z0-9\-\_]/g, '');
}
let block = function(bool) {
return (bool) ? 'block' : 'inline';
}
let safePath = function(path) {
if (path == '..' || path == '.') return '';
return path.replace(/[\/]+/g, '')
}
let setLocation = function(location, key, value) {
var loc = new URL(location).searchParams;
loc.set(key,value);
return loc.toString();
}
let user = 'derv';
export {
checkLength,
checkRegex,
handleSubmit,
handleSubmitGet,
block,
safePath,
setLocation,
user
};

8
src/routes/+layout.js Normal file
View file

@ -0,0 +1,8 @@
/** @type {import('./$types').PageLoad} */
export async function load({ fetch }) {
const res = await fetch(`/api/token`);
const username = await res.json();
return { username: username.data };
}

46
src/routes/+layout.svelte Normal file
View file

@ -0,0 +1,46 @@
<style>
#content {
background: var(--light-2);
height: 100vh;
width: calc(100vw - 50px);
padding: 25px;
padding-top: 0px;
display: flex;
flex-direction: column;
align-items: center;
overflow-y: scroll;
}
:global(:root) {
--dark-1: #2b2f36;
--dark-2: #d8d8d8;
--dark-3: rgb(73, 138, 236);
--light-1: #ffffff;
--light-2: #f8f8f8;
--hyperlink: rgb(139, 171, 219);
font-family: 'Open Sans';
}
:global(a) {
color: var(--hyperlink);
text-decoration: none;
}
:global(input, textarea) {
border: 0;
border-radius: 0.2rem;
box-shadow: 0px 2px 2.5px 0px var(--dark-2);
}
</style>
<div id='content'>
<slot />
</div>

View file

@ -0,0 +1,19 @@
import { user } from '$lib/util.js';
/** @type {import('./$types').PageServerLoad} */
export async function load({ fetch, params, url, cookies, request }) {
const res = await fetch(`https://sanifae.insfa.net/api/token`,{
'method': 'GET', 'headers': {
'cookie': request.headers.get('cookie')
}
});
const userJson = await res.json();
const oc = await fetch(`/api/oc?user=${encodeURIComponent(user)}`,{
'method': 'GET'
}).then(x => x.json());
return { data: userJson.data == user, oc, token: request.headers.get('cookie') };
}

62
src/routes/+page.svelte Normal file
View file

@ -0,0 +1,62 @@
<script>
import Area from '$lib/components/Area.svelte';
import FileUpload from '$lib/components/FileUpload.svelte';
import Button from '$lib/components/Button.svelte';
/** @type {import('./$types').PageData} */
export let data;
let editable = {};
</script>
<style>
img {
max-width: 200px;
max-height: 500px;
margin: 5px;
display: block;
}
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-flow: row;
}
</style>
<Area>
<div slot='header'>
OC Rating Stand
</div>
<div slot='main' >
<p>Put your OC and wait for it to be rated! SVGs (vector), PNGs (bitmap), and other image formats can be uploaded.</p>
<FileUpload />
</div>
<div slot='footer'>
Project of <a href='https://insfa.net/'>INSFA</a>
</div>
</Area>
<Area>
<div slot='header'>
Rated OCs
</div>
<div slot='main' class='wrapper'>
{#each data.oc as ocData}
<div>
<img src='/img/{ocData.id}'>
<p>
<span contenteditable='{data.data}' bind:this={editable[ocData.id]}>
{ocData.rating == '-1' ? 'Unrated' : ocData.rating}
</span> / 10
</p>
<Button clickFunc={async () => await fetch(`/api/rateOc?rating=${encodeURIComponent(editable[ocData.id].textContent)}&id=${encodeURIComponent(ocData.id)}`)}>
Update
</Button>
</div>
{/each}
</div>
<div slot='footer'>
</div>
</Area>

View file

@ -0,0 +1,51 @@
import { backend, backendProxy } from '../../../lib/db/db.js';
/** @type {import('./$types').RequestHandler} */
export async function GET({ url, cookies, params, fetch, request }) {
var formEntries = url.searchParams;
formEntries = [...formEntries,['cookie',request.headers.get('cookie')]];
return await handleReq({
cookies,
params: formEntries,
route: params.route,
fetch
});
}
/** @type {import('./$types').RequestHandler} */
export async function POST({ cookies, request, params, fetch }) {
var formEntries = (await request.formData()).entries();
formEntries = [...formEntries,['cookie',request.headers.get('cookie')]];
return await handleReq({
cookies,
params: formEntries,
route: params.route,
fetch
});
}
async function handleReq({ cookies, params, route, fetch }) {
var backendParams = {cookies,fetch};
for (const [key, value] of params) {
backendParams[key] = value + '';
}
return await mainApi({backendParams, route: route});
}
async function mainApi({backendParams, route, fetch}) {
if (Object.keys(backend).indexOf(route) == -1) {
return new Response(JSON.stringify({success: 'route doesn\'t exist'}));
}
var resData = await backendProxy({ route, backendParams });
return new Response(JSON.stringify(resData));
};

View file

@ -0,0 +1,20 @@
import { readFile } from 'node:fs/promises';
/** @type {import('./$types').RequestHandler} */
export async function GET({ url, cookies, params }) {
var imgName = params['img'];
imgName = imgName.replace(/(\s+)/g, '\\$1');
var res = await readFile(`${process.cwd()}/db/post-${imgName}`);
var response = new Response(res);
var extension = imgName.split('.').pop();
if (extension == 'svg') {
response = new Response(res, {'headers': {
'Content-Type': 'image/svg+xml'
}});
}
return response;
}

View file

@ -0,0 +1,43 @@
<script>
import Area from '$lib/components/Area.svelte';
import { handleSubmitGet } from '$lib/util.js';
export let form = {};
let submitFunc = async e => {
var form = JSON.parse(await handleSubmitGet(e));
document.cookie = `token=${form.data}`
if (form.data)
window.location.href='/';
}
</script>
<Area handleSubmit=''>
<p slot="header">
Log in
</p>
<span slot='main'>
<h2>Login with <a href='https://sanifae.insfa.net/'>Sanifae</a></h2>
<form action='https://sanifae.insfa.net/api/login' on:submit|preventDefault={submitFunc} method='GET'>
<p>
Username: <input name='user'>
</p>
<p>
Password: <input type='password' name='pass'>
</p>
<p>
<input type='submit' value='Log in'>
</p>
</form>
</span>
<p slot="footer">
{#if form?.success}
<p>{form?.success}</p>
{/if}
By using the Sanifae service, you agree to the <a href='https://insfa.net/rules'>Terms of Service</a>.
</p>
</Area>

1
static/YourOC.svg Normal file
View file

@ -0,0 +1 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="137.27948" height="140.24416" viewBox="0,0,137.27948,140.24416"><defs><radialGradient cx="240" cy="180" r="68.63974" gradientUnits="userSpaceOnUse" id="color-1"><stop offset="0" stop-color="#a5ffa5"/><stop offset="1" stop-color="#a5ffa5" stop-opacity="0"/></radialGradient></defs><g transform="translate(-171.36026,-108.39558)"><g data-paper-data="{&quot;isPaintingLayer&quot;:true}" fill-rule="nonzero" stroke="none" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" style="mix-blend-mode: normal"><path d="M171.36026,180c0,-37.90868 30.73107,-68.63974 68.63974,-68.63974c37.90868,0 68.63974,30.73107 68.63974,68.63974c0,37.90868 -30.73107,68.63974 -68.63974,68.63974c-37.90868,0 -68.63974,-30.73107 -68.63974,-68.63974z" fill="url(#color-1)" stroke-width="NaN"/><path d="M192.83702,180c0,-26.04739 21.11559,-47.16298 47.16298,-47.16298c26.04739,0 47.16298,21.11559 47.16298,47.16298c0,26.04739 -21.11559,47.16298 -47.16298,47.16298c-26.04739,0 -47.16298,-21.11559 -47.16298,-47.16298z" fill="#6bad6b" stroke-width="NaN"/><path d="M217.92371,161.18498c0,-12.1924 9.88389,-22.07628 22.07629,-22.07628c12.1924,0 22.07629,9.88388 22.07629,22.07628c0,12.1924 -9.88389,22.07629 -22.07629,22.07629c-12.1924,0 -22.07629,-9.88389 -22.07629,-22.07629z" fill="#c8fcc2" stroke-width="NaN"/><path d="M240,185.26821c26.04739,0 41.51177,20.89527 32.09919,29.51768c-8.39103,7.68661 -19.82278,12.37709 -32.09919,12.37709c-12.64275,0 -24.12363,-4.97459 -32.59202,-13.07318c-8.97873,-8.58664 6.54462,-28.82159 32.59202,-28.82159z" fill="#c8fcc2" stroke-width="NaN"/><text transform="translate(209.02688,124.93559) scale(0.38023,0.38023)" font-size="40" xml:space="preserve" fill="#ffffff" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="Sans Serif" font-weight="normal" text-anchor="start" style="mix-blend-mode: normal"><tspan x="0" dy="0">Your OC</tspan></text></g></g></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

50
static/delete.svg Normal file
View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="39.749512mm"
height="34.423737mm"
viewBox="0 0 39.749511 34.423736"
version="1.1"
id="svg14062"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="delete.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview14064"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="2.0560856"
inkscape:cx="40.367969"
inkscape:cy="68.333731"
inkscape:window-width="1920"
inkscape:window-height="996"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs14059" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-48.152692,-141.96593)">
<path
id="path33180"
style="fill:#aaaaaa;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.96999"
inkscape:transform-center-y="-5.7373187"
d="M -65.658893,-122.24195 -140.77608,7.8635183 H 9.458295 Z m -12.876954,35.990233 h 25.753907 l -3.041016,56.396485 h -19.671875 z m 12.81836,65.816407 c 3.378213,0 6.27876,0.853521 8.703125,2.5625 2.424365,1.708978 3.636719,4.669982 3.636719,8.8828125 0,4.0141122 -1.212354,6.9166124 -3.636719,8.70507821 -2.424365,1.78846589 -5.324912,2.68164069 -8.703125,2.68164069 -3.417957,0 -6.378961,-0.8931748 -8.882813,-2.68164069 -2.464108,-1.78846581 -3.697266,-4.69096601 -3.697266,-8.70507821 0,-4.2128305 1.233158,-7.1738345 3.697266,-8.8828125 2.503852,-1.708979 5.464856,-2.5625 8.882813,-2.5625 z"
transform="matrix(0.26458333,0,0,0.26458333,85.399696,174.30911)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

52
static/downvote.svg Normal file
View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="38.195164mm"
height="62.605831mm"
viewBox="0 0 38.195163 62.605831"
version="1.1"
id="svg14062"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="downvote.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview14064"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="2.0560856"
inkscape:cx="0.48636107"
inkscape:cy="32.829372"
inkscape:window-width="1920"
inkscape:window-height="996"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs14059" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-48.929905,-129.52741)">
<path
style="fill:none;fill-opacity:1;stroke:#aaa;stroke-width:11.90625;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 56.684592,149.59814 10.887303,12.1325 13.600048,-26.2501"
id="path3462" />
<path
style="fill:none;fill-opacity:1;stroke:#aaa;stroke-width:11.90625;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 79.370381,172.06252 68.483078,159.93002 54.88303,186.18012"
id="path8000" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

61
static/icon_sanifae.svg Normal file
View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="69.642113mm"
height="77.026108mm"
viewBox="0 0 69.642113 77.026108"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="template.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.18173401"
inkscape:cx="2517.4155"
inkscape:cy="1532.4595"
inkscape:window-width="1920"
inkscape:window-height="996"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-41.180676,-30.027448)">
<g
id="g20517"
transform="matrix(0.68756186,0,0,0.68756186,-44.467038,-7.6059692)"
inkscape:export-filename="icon_sanifae.svg"
inkscape:export-xdpi="1031.03"
inkscape:export-ydpi="1031.03">
<path
id="path20351"
style="opacity:1;fill:#ffffff;stroke:#e5a8ff;stroke-width:0;stroke-miterlimit:3.96999"
d="m -179.12012,71.140402 a 38.853859,39.608299 0 0 1 38.85342,39.608018 38.853859,39.608299 0 0 1 -38.85342,39.60877 38.853859,39.608299 0 0 1 -38.85418,-39.60877 38.853859,39.608299 0 0 1 38.85418,-39.608018 z m -2.6689,2.086413 c -1.81603,0 -3.30098,1.484955 -3.30098,3.300981 0,1.816023 1.48495,3.301733 3.30098,3.301733 1.81603,0 3.30173,-1.48571 3.30173,-3.301733 0,-1.816026 -1.4857,-3.300981 -3.30173,-3.300981 z m 0,1.220581 c 1.15641,0 2.08115,0.923986 2.08115,2.0804 0,1.15641 -0.92474,2.081152 -2.08115,2.081152 -1.15641,0 -2.0804,-0.924742 -2.0804,-2.081152 0,-1.156414 0.92399,-2.0804 2.0804,-2.0804 z m 10.24266,4.441143 c -9.52749,0 -17.31963,7.79214 -17.31963,17.31963 0,9.527481 7.79214,17.319631 17.31963,17.319631 9.5275,0 17.32039,-7.79215 17.32039,-17.319631 0,-9.52749 -7.79289,-17.31963 -17.32039,-17.31963 z m 0,6.403543 c 6.06693,0 10.91684,4.849162 10.91684,10.916087 0,6.066921 -4.84991,10.916841 -10.91684,10.916841 -6.06692,0 -10.91608,-4.84992 -10.91608,-10.916841 0,-6.066925 4.84916,-10.916087 10.91608,-10.916087 z m -34.39424,8.417803 c -2.94896,0 -5.36109,2.411367 -5.36109,5.360337 0,2.948968 2.41213,5.361088 5.36109,5.361088 2.94898,0 5.36109,-2.41212 5.36109,-5.361088 0,-2.94897 -2.41211,-5.360337 -5.36109,-5.360337 z m 0,1.981942 c 1.87785,0 3.37915,1.500546 3.37915,3.378395 0,1.877858 -1.5013,3.379148 -3.37915,3.379148 -1.87785,0 -3.37914,-1.50129 -3.37914,-3.379148 0,-1.877849 1.50129,-3.378395 3.37914,-3.378395 z m 4.52081,14.621423 c -6.05882,0 -11.01454,4.95497 -11.01454,11.01379 0,6.05882 4.95572,11.01455 11.01454,11.01455 6.05883,0 11.0138,-4.95573 11.0138,-11.01455 0,-6.05882 -4.95497,-11.01379 -11.0138,-11.01379 z m 48.6271,3.30775 c -4.10155,0 -7.45577,3.35422 -7.45577,7.45576 0,4.10155 3.35422,7.45577 7.45577,7.45577 4.10153,0 7.45652,-3.35422 7.45652,-7.45577 0,-4.10154 -3.35499,-7.45576 -7.45652,-7.45576 z m -48.6271,0.76436 c 3.85814,0 6.94169,3.08354 6.94169,6.94168 0,3.85814 -3.08355,6.94244 -6.94169,6.94244 -3.85814,0 -6.94243,-3.0843 -6.94243,-6.94244 0,-3.85814 3.08429,-6.94168 6.94243,-6.94168 z m 25.33008,0.68245 c -5.772,0 -10.49295,4.72093 -10.49295,10.49294 0,5.77199 4.72095,10.49294 10.49295,10.49294 5.772,0 10.49294,-4.72095 10.49294,-10.49294 0,-5.77201 -4.72094,-10.49294 -10.49294,-10.49294 z m 23.29702,1.30927 c 2.61177,0 4.69969,2.0879 4.69969,4.69968 0,2.61179 -2.08792,4.69969 -4.69969,4.69969 -2.61179,0 -4.69969,-2.0879 -4.69969,-4.69969 0,-2.61178 2.0879,-4.69968 4.69969,-4.69968 z m -23.29702,2.57043 c 3.67549,0 6.61323,2.93773 6.61323,6.61324 0,3.67549 -2.93774,6.61323 -6.61323,6.61323 -3.6755,0 -6.61324,-2.93774 -6.61324,-6.61323 0,-3.67551 2.93774,-6.61324 6.61324,-6.61324 z m -14.74093,15.1776 c -2.09552,0 -3.80981,1.71429 -3.80981,3.80981 0,2.09552 1.71429,3.80905 3.80981,3.80905 2.09552,0 3.80906,-1.71353 3.80906,-3.80905 0,-2.09552 -1.71354,-3.80981 -3.80906,-3.80981 z m 0,1.40848 c 1.33439,0 2.40058,1.06694 2.40058,2.40133 0,1.33439 -1.06619,2.40058 -2.40058,2.40058 -1.33439,0 -2.40133,-1.06619 -2.40133,-2.40058 0,-1.33439 1.06694,-2.40133 2.40133,-2.40133 z m 20.57101,5.23708 c -1.99632,0 -3.62943,1.63235 -3.62943,3.62867 0,1.99632 1.63311,3.62943 3.62943,3.62943 1.99631,0 3.62867,-1.63311 3.62867,-3.62943 0,-1.99632 -1.63236,-3.62867 -3.62867,-3.62867 z m 0,1.34158 c 1.27121,0 2.28708,1.01587 2.28708,2.28709 0,1.27122 -1.01587,2.28784 -2.28708,2.28784 -1.27122,0 -2.28709,-1.01662 -2.28709,-2.28784 0,-1.27122 1.01587,-2.28709 2.28709,-2.28709 z"
transform="scale(-1,1)" />
<path
id="ellipse20462"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e5a8ff;stroke-width:0;stroke-miterlimit:3.96999;stroke-dasharray:none;stroke-opacity:1"
d="m 179.51388,54.73459 a 54.946752,56.013673 0 0 0 -54.9466,56.01424 54.946752,56.013673 0 0 0 54.9466,56.01365 54.946752,56.013673 0 0 0 46.32654,-25.93699 l -8.72245,-5.66291 a 44.605491,45.471613 0 0 1 -37.60409,21.05732 44.605491,45.471613 0 0 1 -44.60536,-45.47107 44.605491,45.471613 0 0 1 44.60536,-45.471649 44.605491,45.471613 0 0 1 37.61888,21.048209 l 8.72302,-5.662909 A 54.946752,56.013673 0 0 0 179.51388,54.73459 Z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="69.642113mm"
height="77.026108mm"
viewBox="0 0 69.642113 77.026108"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="template.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.18173401"
inkscape:cx="2517.4155"
inkscape:cy="1532.4595"
inkscape:window-width="1920"
inkscape:window-height="996"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-41.180676,-30.027448)">
<g
id="g20517"
transform="matrix(0.68756186,0,0,0.68756186,-44.467038,-7.6059692)"
inkscape:export-filename="icon_sanifae.svg"
inkscape:export-xdpi="1031.03"
inkscape:export-ydpi="1031.03">
<path
id="path20351"
style="opacity:1;fill:#000000;stroke:#e5a8ff;stroke-width:0;stroke-miterlimit:3.96999"
d="m -179.12012,71.140402 a 38.853859,39.608299 0 0 1 38.85342,39.608018 38.853859,39.608299 0 0 1 -38.85342,39.60877 38.853859,39.608299 0 0 1 -38.85418,-39.60877 38.853859,39.608299 0 0 1 38.85418,-39.608018 z m -2.6689,2.086413 c -1.81603,0 -3.30098,1.484955 -3.30098,3.300981 0,1.816023 1.48495,3.301733 3.30098,3.301733 1.81603,0 3.30173,-1.48571 3.30173,-3.301733 0,-1.816026 -1.4857,-3.300981 -3.30173,-3.300981 z m 0,1.220581 c 1.15641,0 2.08115,0.923986 2.08115,2.0804 0,1.15641 -0.92474,2.081152 -2.08115,2.081152 -1.15641,0 -2.0804,-0.924742 -2.0804,-2.081152 0,-1.156414 0.92399,-2.0804 2.0804,-2.0804 z m 10.24266,4.441143 c -9.52749,0 -17.31963,7.79214 -17.31963,17.31963 0,9.527481 7.79214,17.319631 17.31963,17.319631 9.5275,0 17.32039,-7.79215 17.32039,-17.319631 0,-9.52749 -7.79289,-17.31963 -17.32039,-17.31963 z m 0,6.403543 c 6.06693,0 10.91684,4.849162 10.91684,10.916087 0,6.066921 -4.84991,10.916841 -10.91684,10.916841 -6.06692,0 -10.91608,-4.84992 -10.91608,-10.916841 0,-6.066925 4.84916,-10.916087 10.91608,-10.916087 z m -34.39424,8.417803 c -2.94896,0 -5.36109,2.411367 -5.36109,5.360337 0,2.948968 2.41213,5.361088 5.36109,5.361088 2.94898,0 5.36109,-2.41212 5.36109,-5.361088 0,-2.94897 -2.41211,-5.360337 -5.36109,-5.360337 z m 0,1.981942 c 1.87785,0 3.37915,1.500546 3.37915,3.378395 0,1.877858 -1.5013,3.379148 -3.37915,3.379148 -1.87785,0 -3.37914,-1.50129 -3.37914,-3.379148 0,-1.877849 1.50129,-3.378395 3.37914,-3.378395 z m 4.52081,14.621423 c -6.05882,0 -11.01454,4.95497 -11.01454,11.01379 0,6.05882 4.95572,11.01455 11.01454,11.01455 6.05883,0 11.0138,-4.95573 11.0138,-11.01455 0,-6.05882 -4.95497,-11.01379 -11.0138,-11.01379 z m 48.6271,3.30775 c -4.10155,0 -7.45577,3.35422 -7.45577,7.45576 0,4.10155 3.35422,7.45577 7.45577,7.45577 4.10153,0 7.45652,-3.35422 7.45652,-7.45577 0,-4.10154 -3.35499,-7.45576 -7.45652,-7.45576 z m -48.6271,0.76436 c 3.85814,0 6.94169,3.08354 6.94169,6.94168 0,3.85814 -3.08355,6.94244 -6.94169,6.94244 -3.85814,0 -6.94243,-3.0843 -6.94243,-6.94244 0,-3.85814 3.08429,-6.94168 6.94243,-6.94168 z m 25.33008,0.68245 c -5.772,0 -10.49295,4.72093 -10.49295,10.49294 0,5.77199 4.72095,10.49294 10.49295,10.49294 5.772,0 10.49294,-4.72095 10.49294,-10.49294 0,-5.77201 -4.72094,-10.49294 -10.49294,-10.49294 z m 23.29702,1.30927 c 2.61177,0 4.69969,2.0879 4.69969,4.69968 0,2.61179 -2.08792,4.69969 -4.69969,4.69969 -2.61179,0 -4.69969,-2.0879 -4.69969,-4.69969 0,-2.61178 2.0879,-4.69968 4.69969,-4.69968 z m -23.29702,2.57043 c 3.67549,0 6.61323,2.93773 6.61323,6.61324 0,3.67549 -2.93774,6.61323 -6.61323,6.61323 -3.6755,0 -6.61324,-2.93774 -6.61324,-6.61323 0,-3.67551 2.93774,-6.61324 6.61324,-6.61324 z m -14.74093,15.1776 c -2.09552,0 -3.80981,1.71429 -3.80981,3.80981 0,2.09552 1.71429,3.80905 3.80981,3.80905 2.09552,0 3.80906,-1.71353 3.80906,-3.80905 0,-2.09552 -1.71354,-3.80981 -3.80906,-3.80981 z m 0,1.40848 c 1.33439,0 2.40058,1.06694 2.40058,2.40133 0,1.33439 -1.06619,2.40058 -2.40058,2.40058 -1.33439,0 -2.40133,-1.06619 -2.40133,-2.40058 0,-1.33439 1.06694,-2.40133 2.40133,-2.40133 z m 20.57101,5.23708 c -1.99632,0 -3.62943,1.63235 -3.62943,3.62867 0,1.99632 1.63311,3.62943 3.62943,3.62943 1.99631,0 3.62867,-1.63311 3.62867,-3.62943 0,-1.99632 -1.63236,-3.62867 -3.62867,-3.62867 z m 0,1.34158 c 1.27121,0 2.28708,1.01587 2.28708,2.28709 0,1.27122 -1.01587,2.28784 -2.28708,2.28784 -1.27122,0 -2.28709,-1.01662 -2.28709,-2.28784 0,-1.27122 1.01587,-2.28709 2.28709,-2.28709 z"
transform="scale(-1,1)" />
<path
id="ellipse20462"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#e5a8ff;stroke-width:0;stroke-miterlimit:3.96999;stroke-dasharray:none;stroke-opacity:1"
d="m 179.51388,54.73459 a 54.946752,56.013673 0 0 0 -54.9466,56.01424 54.946752,56.013673 0 0 0 54.9466,56.01365 54.946752,56.013673 0 0 0 46.32654,-25.93699 l -8.72245,-5.66291 a 44.605491,45.471613 0 0 1 -37.60409,21.05732 44.605491,45.471613 0 0 1 -44.60536,-45.47107 44.605491,45.471613 0 0 1 44.60536,-45.471649 44.605491,45.471613 0 0 1 37.61888,21.048209 l 8.72302,-5.662909 A 54.946752,56.013673 0 0 0 179.51388,54.73459 Z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

66
static/logo_sanifae.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

48
static/upvote.svg Normal file
View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32.42485mm"
height="34.187305mm"
viewBox="0 0 32.42485 34.187306"
version="1.1"
id="svg14062"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="upvote.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview14064"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="2.0560856"
inkscape:cx="58.84969"
inkscape:cy="76.601869"
inkscape:window-width="1920"
inkscape:window-height="996"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs14059" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-47.715969,-129.48962)">
<path
style="fill:none;fill-opacity:1;stroke:#aaa;stroke-width:7.9375;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 51.684719,147.57597 10.887303,12.1325 13.600048,-26.2501"
id="path3462" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

54
static/view.svg Normal file
View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="84.203423mm"
height="84.203423mm"
viewBox="0 0 84.203423 84.203423"
version="1.1"
id="svg5"
sodipodi:docname="view.svg"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.72693603"
inkscape:cx="-20.634553"
inkscape:cy="273.06392"
inkscape:window-width="1920"
inkscape:window-height="996"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-53.804537,-76.370715)">
<path
style="color:#000000;fill:#aaa;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.96999;-inkscape-stroke:none"
d="m 95.90625,76.370863 c -23.173772,0 -42.101563,18.92779 -42.101563,42.101567 0,23.17377 18.927791,42.10156 42.101563,42.10156 23.17377,0 42.10156,-18.92779 42.10156,-42.10156 0,-23.173777 -18.92779,-42.101567 -42.10156,-42.101567 z m 0,13.228515 c 16.02419,0 28.87305,12.848862 28.87305,28.873052 0,16.02419 -12.84886,28.87304 -28.87305,28.87304 -16.024189,0 -28.873047,-12.84885 -28.873047,-28.87304 0,-16.02419 12.848858,-28.873052 28.873047,-28.873052 z"
id="path234" />
<circle
style="fill:#aaa;fill-opacity:1;stroke:#aaa;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.96999;stroke-dasharray:none;stroke-opacity:1"
id="path3020"
cx="95.90625"
cy="118.47243"
r="9.6452198" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

10
svelte.config.js Normal file
View file

@ -0,0 +1,10 @@
import adapter from '@sveltejs/adapter-node';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter()
}
};
export default config;

7
vite.config.js Normal file
View file

@ -0,0 +1,7 @@
import { sveltekit } from '@sveltejs/kit/vite';
const config = {
plugins: [sveltekit()]
};
export default config;