mirror of
https://github.com/diced/zipline.git
synced 2025-12-08 13:50:46 -08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d4625f531 | ||
|
|
f5ab288bb3 | ||
|
|
41565b3a62 | ||
|
|
ef7fbaf1dc | ||
|
|
87cf861648 | ||
|
|
d414c85efd | ||
|
|
5b24a8e415 | ||
|
|
1d348db4dd | ||
|
|
ececc3ab0e | ||
|
|
a71bde9730 | ||
|
|
3417a84789 | ||
|
|
080c92a968 | ||
|
|
94b0220db8 | ||
|
|
dc29ad42f5 | ||
|
|
4cce4718a2 | ||
|
|
8fe1de013f | ||
|
|
7a6766e9cc | ||
|
|
5537d28849 | ||
|
|
a420d830e3 |
5
.github/workflows/node.js.yml
vendored
5
.github/workflows/node.js.yml
vendored
@@ -23,5 +23,6 @@ jobs:
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm run build --if-present
|
||||
- run: npm i -g yarn
|
||||
- run: yarn
|
||||
- run: yarn build
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
# Zipline
|
||||
The best and only **React + Next.js** ShareX / File Uploader you would ever want.
|
||||
|
||||
9596
package-lock.json
generated
9596
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zipline-next",
|
||||
"version": "2.2.0",
|
||||
"version": "2.4.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@dicedtomato/colors": "^1.0.3",
|
||||
@@ -61,4 +61,4 @@
|
||||
"ts-node": "^9.0.0",
|
||||
"typescript": "^4.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
release.js
Normal file
3
release.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = async (markdown) => {
|
||||
return markdown;
|
||||
};
|
||||
20
setup.js
20
setup.js
@@ -25,7 +25,7 @@ const base = {
|
||||
'https://github.githubassets.com/images/modules/open_graph/github-mark.png',
|
||||
color: '#128377'
|
||||
},
|
||||
core: { secret: 'my-secret', port: 3000 },
|
||||
core: { secret: 'my-secret', port: 3000, host: '127.0.0.1', theme: 'dark', secure: false },
|
||||
uploader: {
|
||||
directory: './uploads',
|
||||
route: '/u',
|
||||
@@ -48,35 +48,33 @@ const base = {
|
||||
{ name: 'mysql' },
|
||||
{ name: 'mariadb' },
|
||||
{ name: 'mssql' },
|
||||
{ name: 'sqlite' },
|
||||
{ name: 'sqlite3' },
|
||||
{ name: 'mongodb', extra: 'No support yet' }
|
||||
{ name: 'sqlite3' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'host',
|
||||
message: 'Database Host'
|
||||
message: 'Database Host (leave blank if sqlite3)'
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'port',
|
||||
message: 'Database Port'
|
||||
message: 'Database Port (leave blank if sqlite3)'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'database',
|
||||
message: 'Database Name'
|
||||
message: 'Database Name (db path if sqlite3)'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'username',
|
||||
message: 'Database User'
|
||||
message: 'Database User (leave blank if sqlite3)'
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: 'Database Password'
|
||||
message: 'Database Password (leave blank if sqlite3)'
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -106,7 +104,7 @@ const base = {
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'original',
|
||||
message: 'Keep Original?'
|
||||
message: 'Keep Original File names?'
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -138,7 +136,7 @@ const base = {
|
||||
urls: { ...base.urls, ...urls }
|
||||
};
|
||||
|
||||
writeFileSync('Zipline.toml', stringify(config));
|
||||
writeFileSync('Ziplined.toml', stringify(config));
|
||||
|
||||
if (docker.useDocker) {
|
||||
console.log('Generating docker-compose.yml...');
|
||||
|
||||
@@ -34,13 +34,14 @@ export default function ManageUser() {
|
||||
const [alertOpen, setAlertOpen] = useState(false);
|
||||
const [username, setUsername] = useState(state.user.username);
|
||||
const [password, setPassword] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
|
||||
const handleUpdateUser = async () => {
|
||||
const d = await (
|
||||
await fetch('/api/user', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password })
|
||||
body: JSON.stringify({ username, password: password.trim() === '' ? null : password, email })
|
||||
})
|
||||
).json();
|
||||
if (!d.error) {
|
||||
@@ -82,6 +83,14 @@ export default function ManageUser() {
|
||||
className={classes.field}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
label='Email'
|
||||
type='email'
|
||||
helperText='Used for a Gravatar Avatar'
|
||||
value={email}
|
||||
className={classes.field}
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button
|
||||
|
||||
@@ -5,6 +5,7 @@ import Box from '@material-ui/core/Box';
|
||||
import Menu from '@material-ui/core/Menu';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import Drawer from '@material-ui/core/Drawer';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Hidden from '@material-ui/core/Hidden';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
@@ -39,6 +40,7 @@ import { makeStyles, useTheme, withStyles } from '@material-ui/core/styles';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { store } from '../store';
|
||||
import { MD5 } from 'crypto-js';
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
@@ -101,6 +103,7 @@ export default function UI({ children }) {
|
||||
const state = store.getState();
|
||||
const router = useRouter();
|
||||
const dispatch = useDispatch();
|
||||
const [emailHash, setEmailHash] = useState('');
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const [admin, setAdmin] = useState(false);
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
@@ -119,6 +122,8 @@ export default function UI({ children }) {
|
||||
router.push('/user/login');
|
||||
} else setAdmin(d.administrator);
|
||||
})();
|
||||
|
||||
setEmailHash(MD5(state.user.email).toString());
|
||||
}, []);
|
||||
|
||||
const handleCopyTokenThenClose = async () => {
|
||||
@@ -174,16 +179,31 @@ export default function UI({ children }) {
|
||||
</IconButton>
|
||||
<Typography variant='h6'>Zipline</Typography>
|
||||
<Box className={classes.rightButton}>
|
||||
<IconButton
|
||||
aria-label='account of current user'
|
||||
aria-controls='menu-appbar'
|
||||
aria-haspopup='true'
|
||||
onClick={event => setAnchorEl(event.currentTarget)}
|
||||
color='inherit'
|
||||
className={classes.rightButton}
|
||||
>
|
||||
<AccountCircleIcon className={classes.rightButton} />
|
||||
</IconButton>
|
||||
{state.user.email ? (
|
||||
<Button
|
||||
aria-label='account of current user'
|
||||
aria-controls='menu-appbar'
|
||||
aria-haspopup='true'
|
||||
onClick={event => setAnchorEl(event.currentTarget)}
|
||||
color='inherit'
|
||||
className={classes.rightButton}
|
||||
>
|
||||
<Avatar src={`https://www.gravatar.com/avatar/${emailHash}.jpg`}>
|
||||
{state.user.username[0].toUpperCase()}
|
||||
</Avatar>
|
||||
</Button>
|
||||
) : (
|
||||
<IconButton
|
||||
aria-label='account of current user'
|
||||
aria-controls='menu-appbar'
|
||||
aria-haspopup='true'
|
||||
onClick={event => setAnchorEl(event.currentTarget)}
|
||||
color='inherit'
|
||||
className={classes.rightButton}
|
||||
>
|
||||
<AccountCircleIcon className={classes.rightButton} />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Menu
|
||||
|
||||
17
src/index.ts
17
src/index.ts
@@ -12,14 +12,17 @@ import { Console } from './lib/logger';
|
||||
import { AddressInfo } from 'net';
|
||||
import { magenta, bold, green, reset, blue, red } from '@dicedtomato/colors';
|
||||
import { Configuration } from './lib/Config';
|
||||
import { UserController } from './controllers/UserController';
|
||||
import { RootController } from './controllers/RootController';
|
||||
import { UserController } from './lib/api/controllers/UserController';
|
||||
import { RootController } from './lib/api/controllers/RootController';
|
||||
import { join } from 'path';
|
||||
import { ImagesController } from './controllers/ImagesController';
|
||||
import { URLSController } from './controllers/URLSController';
|
||||
import { URL } from './entities/URL';
|
||||
import { ImagesController } from './lib/api/controllers/ImagesController';
|
||||
import { URLSController } from './lib/api/controllers/URLSController';
|
||||
import { checkVersion } from './lib/Util';
|
||||
import { readFileSync } from 'fs';
|
||||
import { Image } from './lib/entities/Image';
|
||||
import { User } from './lib/entities/User';
|
||||
import { Zipline } from './lib/entities/Zipline';
|
||||
import { URL } from './lib/entities/URL';
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
|
||||
(async () => { if (await checkVersion()) Console.logger('Zipline').info('running an outdated version of zipline, please update soon!'); })();
|
||||
@@ -110,7 +113,7 @@ server.register(fastifyMultipart);
|
||||
|
||||
server.register(fastifyTypeorm, {
|
||||
...config.database,
|
||||
entities: [dev ? './src/entities/**/*.ts' : './dist/entities/**/*.js'],
|
||||
entities: [Image, URL, User, Zipline],
|
||||
synchronize: true,
|
||||
logging: false
|
||||
});
|
||||
@@ -163,4 +166,4 @@ server.addHook('preHandler', async (req, reply) => {
|
||||
await app.render404(req.raw, reply.raw);
|
||||
return (reply.sent = true);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import { compareSync, hashSync } from 'bcrypt';
|
||||
import { Configuration } from './Config';
|
||||
import { Connection } from 'typeorm';
|
||||
import { compare } from 'semver';
|
||||
import { Zipline } from '../entities/Zipline';
|
||||
import { Zipline } from './entities/Zipline';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Image } from '../entities/Image';
|
||||
import { URL } from '../entities/URL';
|
||||
import { User } from '../entities/User';
|
||||
import { Image } from './entities/Image';
|
||||
import { URL } from './entities/URL';
|
||||
import { User } from './entities/User';
|
||||
import { Config, Configuration, ConfigWebhooks } from './Config';
|
||||
import { Console } from './logger';
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ import {
|
||||
DELETE
|
||||
} from 'fastify-decorators';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Image } from '../entities/Image';
|
||||
import { LoginError } from '../lib/api/APIErrors';
|
||||
import { Configuration, ConfigWebhooks } from '../lib/Config';
|
||||
import { Console } from '../lib/logger';
|
||||
import { readBaseCookie } from '../lib/Util';
|
||||
import { WebhookHelper, WebhookType } from '../lib/Webhooks';
|
||||
import { Image } from '../../entities/Image';
|
||||
import { LoginError } from '../APIErrors';
|
||||
import { Configuration, ConfigWebhooks } from '../../Config';
|
||||
import { Console } from '../../logger';
|
||||
import { readBaseCookie } from '../../Util';
|
||||
import { WebhookHelper, WebhookType } from '../../Webhooks';
|
||||
|
||||
const config = Configuration.readConfig();
|
||||
|
||||
@@ -12,13 +12,13 @@ import { join } from 'path';
|
||||
import { Repository } from 'typeorm';
|
||||
import { pipeline } from 'stream';
|
||||
import { promisify } from 'util';
|
||||
import { Image } from '../entities/Image';
|
||||
import { User } from '../entities/User';
|
||||
import { AuthError } from '../lib/api/APIErrors';
|
||||
import { Configuration, ConfigWebhooks } from '../lib/Config';
|
||||
import { createRandomId, getFirst } from '../lib/Util';
|
||||
import { Console } from '../lib/logger';
|
||||
import { WebhookHelper, WebhookType } from '../lib/Webhooks';
|
||||
import { Image } from '../../entities/Image';
|
||||
import { User } from '../../entities/User';
|
||||
import { AuthError } from '../APIErrors';
|
||||
import { Configuration, ConfigWebhooks } from '../../Config';
|
||||
import { createRandomId, getFirst } from '../../Util';
|
||||
import { Console } from '../../logger';
|
||||
import { WebhookHelper, WebhookType } from '../../Webhooks';
|
||||
const pump = promisify(pipeline);
|
||||
|
||||
const config = Configuration.readConfig();
|
||||
@@ -8,13 +8,13 @@ import {
|
||||
POST
|
||||
} from 'fastify-decorators';
|
||||
import { Repository } from 'typeorm';
|
||||
import { URL } from '../entities/URL';
|
||||
import { User } from '../entities/User';
|
||||
import { LoginError } from '../lib/api/APIErrors';
|
||||
import { Configuration, ConfigWebhooks } from '../lib/Config';
|
||||
import { Console } from '../lib/logger';
|
||||
import { createRandomId, readBaseCookie } from '../lib/Util';
|
||||
import { WebhookType, WebhookHelper } from '../lib/Webhooks';
|
||||
import { URL } from '../../entities/URL';
|
||||
import { User } from '../../entities/User';
|
||||
import { LoginError } from '../APIErrors';
|
||||
import { Configuration, ConfigWebhooks } from '../../Config';
|
||||
import { Console } from '../../logger';
|
||||
import { createRandomId, readBaseCookie } from '../../Util';
|
||||
import { WebhookType, WebhookHelper } from '../../Webhooks';
|
||||
|
||||
const config = Configuration.readConfig();
|
||||
|
||||
@@ -9,16 +9,16 @@ import {
|
||||
DELETE
|
||||
} from 'fastify-decorators';
|
||||
import { Repository } from 'typeorm';
|
||||
import { User } from '../entities/User';
|
||||
import { Zipline } from '../entities/Zipline';
|
||||
import { User } from '../../entities/User';
|
||||
import { Zipline } from '../../entities/Zipline';
|
||||
import {
|
||||
UserNotFoundError,
|
||||
MissingBodyData,
|
||||
LoginError,
|
||||
UserExistsError
|
||||
} from '../lib/api/APIErrors';
|
||||
import { Configuration, ConfigWebhooks } from '../lib/Config';
|
||||
import { Console } from '../lib/logger';
|
||||
} from '../APIErrors';
|
||||
import { Configuration, ConfigWebhooks } from '../../Config';
|
||||
import { Console } from '../../logger';
|
||||
import {
|
||||
checkPassword,
|
||||
createBaseCookie,
|
||||
@@ -26,8 +26,8 @@ import {
|
||||
encryptPassword,
|
||||
getFirst,
|
||||
readBaseCookie
|
||||
} from '../lib/Util';
|
||||
import { WebhookType, WebhookHelper } from '../lib/Webhooks';
|
||||
} from '../../Util';
|
||||
import { WebhookType, WebhookHelper } from '../../Webhooks';
|
||||
|
||||
const config = Configuration.readConfig();
|
||||
|
||||
@@ -63,7 +63,7 @@ export class UserController {
|
||||
|
||||
@PATCH('/')
|
||||
async editUser(
|
||||
req: FastifyRequest<{ Body: { username: string; password: string } }>,
|
||||
req: FastifyRequest<{ Body: { username: string; password: string, email: string; } }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
if (!req.cookies.zipline) throw new LoginError('Not logged in.');
|
||||
@@ -78,7 +78,8 @@ export class UserController {
|
||||
|
||||
this.logger.verbose(`attempting to save ${user.username} (${user.id})`);
|
||||
user.username = req.body.username;
|
||||
user.password = encryptPassword(req.body.password);
|
||||
if (req.body.password) user.password = encryptPassword(req.body.password);
|
||||
if (req.body.email) user.email = req.body.email;
|
||||
await this.users.save(user);
|
||||
|
||||
this.logger.info(`saved ${user.username} (${user.id})`);
|
||||
@@ -236,9 +237,7 @@ export class UserController {
|
||||
this.logger.verbose(
|
||||
`attempting to delete ${existing.username} (${existing.id})`
|
||||
);
|
||||
await this.users.delete({
|
||||
id: existing.id
|
||||
});
|
||||
await this.users.remove(existing);
|
||||
|
||||
this.logger.info(`deleted ${existing.username} (${existing.id})`);
|
||||
if (this.webhooks.events.includes(WebhookType.USER_DELETE))
|
||||
@@ -11,6 +11,9 @@ export class User {
|
||||
@Column('text')
|
||||
public password: string;
|
||||
|
||||
@Column('text', { default: null }) /* used for gravatar avatar! */
|
||||
public email: string;
|
||||
|
||||
@Column('boolean', { default: false })
|
||||
public administrator: boolean;
|
||||
|
||||
@@ -25,6 +28,7 @@ export class User {
|
||||
) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.email = null;
|
||||
this.administrator = administrator;
|
||||
this.token = token;
|
||||
}
|
||||
@@ -9,6 +9,7 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!global.logr) global.logr = { formatter: null };
|
||||
|
||||
export enum ConsoleLevel {
|
||||
|
||||
@@ -16,7 +16,7 @@ import UI from '../../components/UI';
|
||||
import UIPlaceholder from '../../components/UIPlaceholder';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { store } from '../../store';
|
||||
import { Image } from '../../entities/Image';
|
||||
import { Image } from '../../lib/entities/Image';
|
||||
import { Configuration } from '../../lib/Config';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
|
||||
@@ -24,7 +24,7 @@ import copy from 'copy-to-clipboard';
|
||||
import UI from '../../components/UI';
|
||||
import UIPlaceholder from '../../components/UIPlaceholder';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { URL as URLEntity } from '../../entities/URL';
|
||||
import { URL as URLEntity } from '../../lib/entities/URL';
|
||||
import { Configuration } from '../../lib/Config';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable indent */
|
||||
import { User } from './entities/User';
|
||||
import { User } from './lib/entities/User';
|
||||
|
||||
export const LOGIN = 'LOGIN';
|
||||
export const LOGOUT = 'LOGOUT';
|
||||
|
||||
Reference in New Issue
Block a user